-
Bonjour :)
Je viens de débuter sur SFML
Je souhaiterai convertir mon projet de jeux 2d ( basé sur un affichage de tileset et de sprite ) en PyGame, loin d'être fini mais PyGame n’étant pas assez véloce dans certain domaine je voulais essayer SFML pour comparer. Puis apparemment cette librairie offre plus de possibilité :)
J’essaie de traduire le tuto en C++ des vertexarray ( celui qui charge un tileset et l'affiche ) mais je galère quand même pas mal
Voila l'url du tuto C++
http://www.sfml-dev.org/tutorials/2.1/graphics-vertex-array-fr.php (http://www.sfml-dev.org/tutorials/2.1/graphics-vertex-array-fr.php)
et voici mon début de traduction :
import sfml as sf
class TileMap (sf.Drawable) :
def __init__ (self):
sfml.Drawable.__init__(self)
# on declare la texture et le VertexArray
self.m_tileset = sf.Texture
self.m_vertices = sf.VertexArray(sf.PrimitiveType.QUADS)
def load (self,tileset, tileSize, tiles, width, height) :
# on charge la texture
self.m_tileset = sf.Texture.from_file(tileset)
# on redimensionne le tableau de vertex pour qu'il puisse contenir tout le niveau
self.m_vertices.resize(width * height * 4);
# on remplit le tableau de vertex, avec un quad par tuile
for i in range(width) :
for j in range(height) :
# on récupère le numéro de tuile courant
tileNumber = tiles[i + j * width];
# on en déduit sa position dans la texture du tileset
#
# déjà la ça déconne ...
#
tu = tileNumber % (self.m_tileset.size.x / tileSize.x);
tv = tileNumber // (self.m_tileset.size.x / tileSize.x);
# on récupère un pointeur vers le quad à définir dans le tableau de vertex
self.quad = self.m_vertices[(i + j * width) * 4]
print(type(self.quad))
# on définit ses quatre coins
#
# C'est la que je ne comprend pas ... un type(self.quad) m'indique que c'est un vertex
# hors un vertex ne n'est pas iterable donc d’où vient le quad[2]
#
self.quad[0].position = sf.Vector2(i * tileSize.x, j * tileSize.y)
self.quad[1].position = sf.Vector2((i + 1) * tileSize.x, j * tileSize.y)
self.quad[2].position = sf.Vector2((i + 1) * tileSize.x, (j + 1) * tileSize.y)
self.quad[3].position = sf.Vector2(i * tileSize.x, (j + 1) * tileSize.y)
# on définit ses quatre coordonnées de texture
self.quad[0].tex_coords = sf.Vector2(tu * tileSize.x, tv * tileSize.y)
self.quad[1].tex_coords = sf.Vector2((tu + 1) * tileSize.x, tv * tileSize.y)
self.quad[2].tex_coords = sf.Vector2((tu + 1) * tileSize.x, (tv + 1) * tileSize.y)
self.quad[3].tex_coords = sf.Vector2(tu * tileSize.x, (tv + 1) * tileSize.y)
# On crée la fenêtre principale
window = sf.RenderWindow(sf.VideoMode(800, 600), "PySFML test")
# window.vertical_synchronization = True
# window.framerate_limit = 60
# icon = sf.Image.from_file("data/icon.bmp")
# window.icon = icon.pixels
level = [
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
]
tm = TileMap()
tm.load("resources/tileset1a.jpg",sf.Vector2(32,32),level,16,8)
window.draw(tm)
window.display()
Je ne comprend pas cette histoire de quad, qui est un vertex et qui serait iterrable ???
version C++ :
// on récupère un pointeur vers le quad à définir dans le tableau de vertex
sf::Vertex* quad = &m_vertices[(i + j * width) * 4];
// on définit ses quatre coins
quad[0].position = sf::Vector2f(i * tileSize.x, j * tileSize.y);
-
Dans la version C++, c'est un pointeur sur un sf::Vertex qu'on a, pas un sf::Vertex. Et avec un pointeur on peut se balader pour aller pointer sur les élements (vertex) suivants dans le tableau. Dans ta version Python, je ne suis pas sûr que tu puisses avoir l'équivalent.
De toute façon ce n'est pas nécessaire, c'est juste pour ne pas écrire 8 fois le calcul d'indice complet. Tu peux faire ça pour t'en passer :
base = (i + j * width) * 4;
self.m_vertices[base + 0].position = ...;
self.m_vertices[base + 1].position = ...;
self.m_vertices[base + 2].position = ...;
self.m_vertices[base + 3].position = ...;
self.m_vertices[base + 0].texCoords = ...;
self.m_vertices[base + 1].texCoords = ...;
self.m_vertices[base + 2].texCoords = ...;
self.m_vertices[base + 3].texCoords = ...;
-
Merci pour ta réponse :) je vais essayer ça
J'en profite, pour un affichage en tuile, ( comme dans le tuto) il est préférable d'utiliser les tableau de vertex ou un affichage de multiple sprites fonctionne aussi très bien ?
-
Les sprites seront bien moins efficaces. Comme expliqué dans le tutoriel.
-
J'ai fait ce que tu m'a dit :) il n'y pas d'erreur mais il n'y a pas d'affichage non plus.
Je pense qu'il y a un truc qui rechape avec la méthode draw de la classe Tilemap et la methode sf.draw originale aussi
ce code fonctionne et m’affiche un jolie rectangle avec un dégradé de couleur :
import sfml as sf
window = sf.RenderWindow(sf.VideoMode(640, 480), 'SFML Window')
quad = sf.VertexArray(sf.PrimitiveType.QUADS, 4)
quad[0].position = (10, 10)
quad[1].position = (110, 10)
quad[2].position = (110, 110)
quad[3].position = (10, 110)
quad[0].color = sf.Color.RED
quad[1].color = sf.Color.GREEN
quad[2].color = sf.Color.BLUE
quad[3].color = sf.Color.YELLOW
window.draw(quad)
Mais pour appliquer une texture, je pensai qu'il suffisait de passer cette texture en paramètre comme l'indique le tuto
sf::VertexArray vertices;
sf::Texture texture;
...
window.draw(vertices, &texture);
donc :
tileset = sf.Texture.from_file("resources/tileset1a.jpg")
window.draw(quad, tileset)
Erreur :
TypeError: Argument 'states' has incorrect type (expected sfml.graphics.RenderStates,
got sfml.graphics.Texture)
Il faut passer un renderStates ? et la ...
quad = sf.VertexArray(sf.PrimitiveType.QUADS, 4)
quad[0].position = (10, 10)
quad[1].position = (110, 10)
quad[2].position = (110, 110)
quad[3].position = (10, 110)
quad[0].color = sf.Color.RED
quad[1].color = sf.Color.GREEN
quad[2].color = sf.Color.BLUE
quad[3].color = sf.Color.YELLOW
quad[0].tex_coords = (0, 0);
quad[1].tex_coords = (25, 0);
quad[2].tex_coords = (25, 50);
quad[3].tex_coords = (0, 50);
tileset = sf.Texture.from_file("resources/tileset1a.jpg")
states = sf.RenderStates
states.texture = tileset
window.draw(quad,states)
-
Là je ne peux pas t'aider, le type de paramètre et la manière de le passer sont spécifiques au binding Python.
Mais ton dernier morceau de code a l'air ok, ça ne fonctionne pas non plus ?
-
et non :)
Voici le message d'erreur concernant la ligne : states.texture = tileset
TypeError: can't set attributes of built-in/extension type 'sfml.graphics.RenderStates'
EDIT :
ahh j'ai vue d’où provient une des erreurs :
states = sf.RenderStates()
Je vais déjà chercher de ce coté la :)
EDIT 2 :
J'arrive a afficher la map :)
Voici le code pour les intéressés :
import sfml as sf
class TileMap (sf.TransformableDrawable) : # sf.Transformable
def __init__ (self):
sf.Drawable.__init__(self)
# on declare la texture et le VertexArray
self.m_tileset = sf.Texture
self.m_vertices = sf.VertexArray(sf.PrimitiveType.QUADS)
def load (self,tileset, tileSize, tiles, width, height) :
# on charge la texture
self.m_tileset = sf.Texture.from_file(tileset)
# on redimensionne le tableau de vertex pour qu'il puisse contenir tout le niveau
self.m_vertices.resize(width * height * 4);
# on remplit le tableau de vertex, avec un quad par tuile
for i in range(width) :
for j in range(height) :
# on récupère le numéro de tuile courant
tileNumber = tiles[i + j * width];
# on en déduit sa position dans la texture du tileset
tu = tileNumber % (self.m_tileset.size.x / tileSize.x);
tv = tileNumber // (self.m_tileset.size.x / tileSize.x);
# On incremante une variable qui definie la position de chaque case
case = (i + j * width) * 4;
# on définit ses quatre coins
self.m_vertices[case + 0].position = sf.Vector2(i * tileSize.x, j * tileSize.y)
self.m_vertices[case + 1].position = sf.Vector2((i + 1) * tileSize.x, j * tileSize.y)
self.m_vertices[case + 2].position = sf.Vector2((i + 1) * tileSize.x, (j + 1) * tileSize.y)
self.m_vertices[case + 3].position = sf.Vector2(i * tileSize.x, (j + 1) * tileSize.y)
# on définit ses quatre coordonnées de texture
self.m_vertices[case + 0].tex_coords = sf.Vector2(tu * tileSize.x, tv * tileSize.y)
self.m_vertices[case + 1].tex_coords = sf.Vector2((tu + 1) * tileSize.x, tv * tileSize.y)
self.m_vertices[case + 2].tex_coords = sf.Vector2((tu + 1) * tileSize.x, (tv + 1) * tileSize.y)
self.m_vertices[case + 3].tex_coords = sf.Vector2(tu * tileSize.x, (tv + 1) * tileSize.y)
def draw(self, target) :
states = sf.RenderStates()
states.texture = self.m_tileset
target.draw(self.m_vertices, states)
# ----- MAIN -----
# On crée la fenêtre principale
window = sf.RenderWindow(sf.VideoMode(800, 600), "PySFML test")
level = [
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
]
# on crée la tilemap avec le niveau précédemment défini
map = TileMap()
map.load("resources/tileset1a.jpg", sf.Vector2(32,32),level,16,8)
# On démarre la boucle de jeu
while window.is_open :
# on gère les évènements
for event in window.events :
if type(event) is sf.CloseEvent :
window.close()
# On dessine le niveau
window.clear()
map.draw(window)
window.display()
-
Il me reste une question, sur la methode draw de la classe Tilemap du tuto :
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
// on applique la transformation
states.transform *= getTransform();
// on applique la texture du tileset
states.texture = &m_tileset;
// et on dessine enfin le tableau de vertex
target.draw(m_vertices, [b]states[/b]);
}
Je ne comprend pas comment est elle appelée puisque dans Main() on a un simple :
window.draw(map);
puis le paramètre sf::RenderStates statesje ne voie nul pars ou il est envoyé :/
en python je suis obligé d'appeler la methode classiquement :
map.draw(window)
et sans passer le paramètre states
-
Je ne comprend pas comment est elle appelée
puis le paramètre sf::RenderStates statesje ne voie nul pars ou il est envoyé :/
Je pense qu'une vue (simplifiée) du contenu de RenderWindow::draw répondra à tes questions :
void RenderWindow::draw(sf::Drawable& drawable, sf::RenderStates states = sf::RenderStates::Default) // paramètre optionnel
{
drawable.draw(this, states);
}
Donc :
window.draw(object);
// équivaut à :
sf::RenderStates states = sf::RenderStates::Default;
window.draw(object, states);
// et tout ceci finit en :
object.draw(window, states);
-
C'est vraiment sympa de m'expliquer :) , j'ai tenté de comprendre mais je pense que mon niveau est à des années lumière du tient :'(
Je ne connais absolument pas C++ dont la syntaxe est assez exotique pour moi, et comparé aux langages plus simple que je connais un peu (python, php)
En c++ apparemment on doit déclarer les variables sans le fameux = et en indiquant obligatoirement le type en avant celle ci. Il y a une bestiole étrange " :: " qui a l'air de dire " je suis un membre de la classe ... " et donc remplace la simple notation pointé. Et le void, je crois que je l'ai déjà vue en AS3 ^^ et veut dire " je renvoie rien " . Bref C++ c'est hard pour les petits esprits comme le miens, je vais pas t’embêter à m'expliquer ce langage on sortirai du cadre de ce forum
Mais en python quand j'envoie : window.draw(objet) ça n'appelle pas la méthode draw de cet objet :/
Du coup mon code map.draw(window) dans la boucle while est une mauvaise manière d'utiliser la classe Tilemap ?
Je vais re relire la doc de pysmfl au sujet de renderWindow
-
Là encore, c'est spécifique au binding, je ne peux pas t'aider. Il y a des choses qui fonctionnent en C++ mais ne sont pas reproduites dans certains bindings, notamment les histoires d'héritage.
-
Ça fonctionne sans l’héritage donc je vais faire sans :) et je reviendrai dessus quand j'aurai plus d’expérience.
Je compte tenir un blog pour montrer toutes les étapes de A à Z du développement de mon jeu avec Python et Sfml, et en français évidement. C'est pour ça que j’essaie de ne pas trop contourner les problèmes mais de les comprendre et les repoudre. J'ai pas fini de venir sur ce forum ^^
En tout cas merci pour tes explications et ton aide :)