Part of pictures, articles, images on this page are copyrighted by Mojang AB.
Il s'agit d'un projet visant à reproduire le jeu de bac à sable mondialement connu "Minecraft" dans le langage de programmation "Python".
** Article précédent: "[# 1] Créer Minecraft avec Python.-Recherche préliminaire et conception-" **
** Article suivant: "[# 3] Make Minecraft with Python.-Amélioration du mouvement du joueur (concept d'inertie) et du jugement de collision-" ** **
Merci beaucoup pour votre patience. C'est la seconde!
--Sélection du moteur de jeu --Exécution du procès
Je ne pouvais pas créer un moteur de jeu 3D à partir de rien, alors j'ai cherché un moteur de jeu 3D (bibliothèque) qui prend également en charge Python.
Parmi eux, nous avons sélectionné ceux qui sont relativement faciles à utiliser et qui sont susceptibles de produire de beaux dessins.
Panda3D Engine
C'est un moteur de jeu 3D appelé "Panda3D". Les plates-formes sont Python et C ++.
Je vais en fait le déplacer.
▼ Installation
pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d
▼ Code source
main.py
from Renderer import engine
def main():
_render = engine.Renderer()
_render.run()
if __name__ == "__main__":
main()
engine.py
from direct.showbase.ShowBase import ShowBase
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import TextNode
from pandac.PandaModules import WindowProperties
class Renderer(ShowBase):
def __init__(self):
ShowBase.__init__(self)
props = WindowProperties()
props.setTitle('PyCraft')
props.setSize(1280, 720)
self.win.requestProperties(props)
OnscreenText(text="PyCraft ScreenText",
parent=None, align=TextNode.ARight,
fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.08,
shadow=(0, 0, 0, 0.5))
Il a été dessiné comme ça.
engine.py
from direct.showbase.ShowBase import ShowBase
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import TextNode
from pandac.PandaModules import WindowProperties
from direct.showbase.Loader import Loader
class Renderer(ShowBase):
def __init__(self):
ShowBase.__init__(self)
props = WindowProperties()
props.setTitle('PyCraft')
props.setSize(1280, 720)
self.win.requestProperties(props)
OnscreenText(text="PyCraft ScreenText",
parent=None, align=TextNode.ARight,
fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.08,
shadow=(0, 0, 0, 0.5))
self.scene = self.loader.loadModel("models/environment")
self.scene.reparentTo(self.render)
self.scene.setScale(1, 1, 1)
self.scene.setPos(0, 0, 0)
self.cube = self.loader.loadModel("models/misc/rgbCube")
self.cube.reparentTo(self.render)
self.cube.setScale(1, 1, 1)
self.cube.setPos(0, 20, 0)
Il s'est avéré être quelque chose comme ça. Il semble que vous puissiez l'utiliser avec une souris. Le cube ressemble à ceci.
Référence: vidéo de 6 secondes avec gif @ panda3D
▼ Code source Puisqu'elle sera longue, seule la partie noyau sera décrite.
engine.py
self.world = BulletWorld()
self.world.setGravity(Vec3(0, 0, -9.81))
self.worldPath = self.render.attachNewNode("")
debugNode = BulletDebugNode()
nodePath = self.worldPath.attachNewNode(debugNode)
nodePath.show()
self.world.setDebugNode(debugNode)
bodyGround = BulletRigidBodyNode()
bodyGround.addShape(BulletBoxShape((3, 3, 0.001)))
nodePath = self.worldPath.attachNewNode(bodyGround)
nodePath.setPos(0, 0, -2)
nodePath.setHpr(0, 12, 0)
self.world.attachRigidBody(bodyGround)
self.boxes = []
for i in range(30):
bodyBox = BulletRigidBodyNode()
bodyBox.setMass(1.0)
bodyBox.addShape(BulletBoxShape((0.5, 0.5, 0.5)))
nodePath = self.worldPath.attachNewNode(bodyBox)
nodePath.setPos(0, 0, 2 + i * 2)
self.boxes.append(nodePath)
self.world.attachRigidBody(bodyBox)
Ogre3D "Ogre3D" est l'un des moteurs de jeu qui utilisent l'accélération matérielle. Il semble qu'il prend en charge non seulement C ++ mais aussi Python et .NET. La réalisation de jeux 3D sur .NET est assez difficile, j'aimerais donc utiliser Ogre.NET pour réaliser "J'ai essayé de reproduire Minecraft avec C #!".
Jetez un œil à Showcase.
Cela semble être un jeu comme "X-Morph: Defense". Bien sûr, le moteur Ogre est utilisé. C'est un super graphique.
En plus des moteurs de jeu autres que ceux ci-dessus, j'ai décidé d'utiliser "Pyglet", qui semble avoir le plus d'informations, étant donné qu'il est multiplateforme. Cela ne dépend pas des autres bibliothèques. ・ Il est parfait qu'il prenne en charge le multi-affichage et le multi-moniteur.
À partir de là, faisons un dessin simple en utilisant Pyglet.
▼ Installation
pip install pyglet
▼ Installation (pour PyCharm)
Barre de menus
▶ Fichier
▶ Paramètres
▼ Exemple de code
main.py
#Module d'importation
from pyglet.gl import *
#Fenêtre à afficher
#width est la largeur et la hauteur est la hauteur.
#caption est le titre de la fenêtre et redimensionnable indique s'il faut autoriser le redimensionnement.
pyglet.window.Window(width=700, height=400, caption='Hello Pyglet!', resizable=True)
pyglet.app.run()
S'il est affiché comme ça, c'est OK.
OpenGL est utilisé dans Pyglet.
▼ Exemple de code
main.py
#Module d'importation
from pyglet.gl import *
#fenêtre
window = pyglet.window.Window(width=700, height=400, caption='Hello Pyglet!', resizable=True)
#Événement de dessin de fenêtre
@window.event
def on_draw():
#Dessin clair
window.clear()
glBegin(GL_LINES) #Commencez à dessiner une ligne
glColor3f(1, 0, 0) # R,G,B ÷ 255
glVertex2f(0, 0) # x, y(0, 0)De
glVertex2f(100, 100) # x, y(100, 100)Jusqu'à ce que
glEnd() #Terminez de dessiner la ligne
pyglet.app.run()
Une fois exécutée, la ligne sera dessinée comme ceci.
Dans le code source ci-dessus, la fonction d'événement de dessin de window
est définie et la ligne est dessinée à l'aide d'OpenGL.
Il semble qu'OpenGL reconnaît le coin inférieur gauche de l'écran comme des coordonnées nulles.
** 1. Déclarez le début du dessin au trait avec glBegin (GL_LINES)
**
** 2. Déclarez la couleur avec glColor3f (1, 0, 0)
**
La couleur ici est RVB (rouge, vert, bleu).
Chacun est généralement compris entre 0 et 255, mais ici il est déclaré avec Float
comme il est dit 3f
, alors passez le nombre divisé par 255.f
comme argument.
«3f» signifie passer les nombres «3» dans «f».
** 2. Déclarez le point de départ de la ligne avec glVertex2f (x, y)
**
Passez les coordonnées du point de départ de la ligne.
«2f» signifie passer les coordonnées de la dimension «2» avec «f» loat.
** 3. Déclarez le point final de la ligne avec glVertex2f (x, y)
**
Passez les coordonnées du point final de la ligne.
Il est acceptable de passer ici un nombre plus grand que la taille de la fenêtre, mais il ne sera pas dessiné hors de l'écran.
** 4. Déclarez la fin du dessin au trait avec glEnd ()
**
Enfin le sujet principal.
C'est une ** grosse erreur ** d'essayer de tout implémenter à la fois pour réaliser le projet.
Supposons que vous souhaitiez implémenter la fonction A
, la fonction B
et la fonction C
.
Si ceux-ci sont tous implémentés en même temps et qu'un bogue ou un défaut se produit, il est difficile d'identifier quelle fonction (quelle partie) est à l'origine du bogue ou du défaut, ou il faut plus de temps que d'habitude pour en trouver la cause. Je le ferai.
La procédure correcte est
Mise en œuvre de la fonction A
▶ ** Contrôle de fonctionnement ** ▶ Mise en œuvre de la fonction B
▶ ** Contrôle de fonctionnement ** ▶ Mise en œuvre de la fonction C
est.
Dans le cas peu probable où un problème surviendrait après l'implémentation de la «fonction C», le problème est survenu entre la «fonction B» et la «fonction C». Peut être considéré comme. C'est plus efficace.
Commençons par vérifier le fonctionnement avec la configuration minimale. L'introduction est longue.
Pour le moment, nous l'utiliserons à titre d'essai, nous avons donc préparé cette texture, familière à Minecraft. ▼ Texture
Dessinons réellement en utilisant la texture préparée! Le code source sera long, je l'ai donc posté sur Gist.
▼ Code source Gist: main.py
Une fois exécuté, il sera dessiné comme suit. * Condition normale. </ font>
Pour le moment, je n'ai pu dessiner que d'un seul côté.
Je vais vous expliquer en extrayant des pièces.
Définit ce qu'on appelle un Batch
qui contient le Vertex
à dessiner.
self.batch = pyglet.graphics.Batch()
Ensuite, définissez les coordonnées tridimensionnelles qui sont la clé. Ici, s'il est réglé sur «0», il sera dessiné hors de l'écran ** et ne peut pas être confirmé ** en raison de l'emplacement de la caméra initiale, il est donc défini avec un léger décalage.
#Définir les coordonnées du monde 3D
x = 0.5
y = 0
z = -2
Une fonction est fournie pour charger les textures.
Utilisez le chargeur natif pyglet
.
Spécifiez le patch d'image pour path
.
#Fonction pour charger la texture
def load_texture(self, path):
texture = pyglet.image.load(path).get_texture()#Utilisez le chargeur de texture de Pyglet
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
return pyglet.graphics.TextureGroup(texture)
Vient ensuite la classe window.
Cette classe hérite de la classe pyglet.window.Window
.
La fonction d'initialisation initialise instance de la classe «World» définie précédemment.
super
est similaire à Java.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#Initialisation de classe mondiale
self.world = World()
Ensuite, définissez la fonction qui exécute le dessin essentiel.
def on_draw(self):
#Effacer l'écran
self.clear()
#Puisque le monde est en 3D, réglez-le en mode de dessin 3D
self.render_mode_3d()
#Dessine le monde
self.world.draw()
Voici la fonction render_mode_3d ()
,
Il est nécessaire de définir le mode matrice lors de l'exécution d'un dessin 3D et d'un dessin 2D.
Il a ** Projection
** et Mode de conversion de champ (Modelview)
, qui par défaut en mode de conversion de champ.
Nous discuterons de ces modes matriciels plus tard.
Remarque: Utilisez glLoadIdentity ()
pour effacer (initialiser) le processus de conversion en mode cumulatif. </ font>
def render_mode_3d(self):
self.render_mode_projection()
#Définir le champ de vision
gluPerspective(70, self.width / self.height, 0.05, 1000)
self.render_mode_modelview()
def render_mode_2d(self):
self.render_mode_projection()
#Zone de dessin 0 à fenêtre_largeur, 0 à la fenêtre_height
gluOrtho2D(0, self.width, 0, self.height)
self.render_mode_modelview()
def render_mode_projection(self):
glMatrixMode(GL_PROJECTION)#Mode de conversion de projection
glLoadIdentity()#Éliminez le processus de conversion cumulatif
def render_mode_modelview(self):
glMatrixMode(GL_MODELVIEW)#Modélisation du mode de conversion(Conversion de champ)
glLoadIdentity()#Éliminez le processus de conversion cumulatif
gluOrtho2D
effectue une conversion de projection, qui est une projection parallèle en 2D.
gluOrtho2D(left, right, top, bottom)
Ensuite, gluPerspective ()
définit le champ de vision.
gluPerspective(fovY, aspect, zNear, zFar)
L'angle de vision de l'axe Y, l'aspect (angle de vision horizontal), la distance la plus courte Z et la distance la plus longue Z, respectivement.
De plus, «zNear» et «zFar» ne modifient pas l'apparence à l'écran. Cela peut être considéré comme ** si vous êtes 1 m plus près de l'écran ou 1 m plus près de l'écran **.
À partir de là, le contenu sera spécialisé, donc si vous n'êtes pas intéressé, vous pouvez l'ignorer.
Les modes matriciels incluent ** transformation visuelle (GL_PROJECTION)
** et ** transformation de modélisation (GL_MODELVIEW)
**.
* Pour être exact, il y a aussi GL_TEXTURE
, mais il n'est pas géré dans cette source, alors sautez-le. </ font>
De plus, la conversion 3D ▶ 2D est une «conversion de géométrie» ʻAffin conversion` pour la conversion lors de la mise à l'échelle / du déplacement Est appelé.
Ça va être super long et je pense que certaines personnes sont intéressées, alors je vais le jeter dans un article externe. Pardon.
Je n'ai pas trouvé d'article japonais expliquant en détail la conversion de géométrie.
Je voudrais dessiner le bloc entier, mais c'est gênant car je ne peux pas déplacer le point de vue tel quel, donc pour le moment, je vais implémenter le mouvement du point de vue comme le joueur. J'ai également utilisé "PyImGui" pour afficher les informations de débogage.
▼ Code source Gist: main.py
▼ Comme ça
Maintenant que vous pouvez déplacer librement le point de vue et les coordonnées, dessinons le bloc entier. Le code source est le suivant. Je suis désolé si j'ai fait une erreur dans la direction. ~~ J'ai peur, alors je m'excuse à l'avance. ~~
▼ Code source Gist: main.py
#Je suis désolé si je fais une erreur
#De face
self.batch.add(4, GL_QUADS, self.loaded_texture, ('v3f', (x, y, z, x, y, z+1, x, y+1, z+1, x, y+1, z,)), texture_coordinates)
#Arrière
self.batch.add(4, GL_QUADS, self.loaded_texture, ('v3f', (x+1, y, z+1, x+1, y, z, x+1, y+1, z, x+1, y+1, z+1,)), texture_coordinates)
#Surface inférieure
self.batch.add(4, GL_QUADS, self.loaded_texture, ('v3f', (x, y, z, x+1, y, z, x+1, y, z+1, x, y, z+1,)), texture_coordinates)
#Surface supérieure
self.batch.add(4, GL_QUADS, self.loaded_texture, ('v3f', (x, y+1, z+1, x+1, y+1, z+1, x+1, y+1, z, x, y+1, z,)), texture_coordinates)
#Côté droit
self.batch.add(4, GL_QUADS, self.loaded_texture, ('v3f', (x+1, y, z, x, y, z, x, y+1, z, x+1, y+1, z,)), texture_coordinates)
#la gauche
self.batch.add(4, GL_QUADS, self.loaded_texture, ('v3f', (x, y, z+1, x+1, y, z+1, x+1, y+1, z+1, x, y+1, z+1,)), texture_coordinates)
▼ Ça ressemble à ça
Au fait, comme vous pouvez le voir sur la vidéo, il y a des anomalies dans le dessin.
Ceci est dû au fait que * des pièces supplémentaires * telles que la partie normalement invisible (surface arrière) sont dessinées.
Pour éviter cela, définissez glEnable (GL_DEPTH_TEST)
.
C'est un moyen pratique de sélectionner / d'éliminer le dessin de surface inutile du côté OpenGL.
▼ Après le réglage, il était magnifiquement dessiné!
Cette fois, nous avons sélectionné le moteur de jeu, dessiné le modèle et implémenté le joueur. La prochaine fois, j'aimerais construire un monde et mettre en œuvre le jugement de frappe.
Merci d'avoir regardé jusqu'à la fin.