Skip to main content

Moteur de jeu 2D sur PyOpenGL (GLFW + Pillow)

Project description

Tiavina.engine.gl

Moteur de jeu 2D pixel art, construit sur PyOpenGL + GLFW + Pillow.
Fonctionne avec un écran virtuel (résolution logique) mis à l'échelle entière vers la fenêtre d'affichage.

Installation

pip install Tiavina.engine.gl
from Engine import engine as e

e.init(200, 200, "Mon Jeu", fps=60, display_scale=4)

def update():
    if e.btn(e.KEY_ESCAPE):
        e.quit()

def draw():
    e.cls((30, 30, 40))
    e.rect(10, 10, 20, 20, (255, 0, 0))

e.run(update, draw)

Table des matières


Architecture

Engine/
├── __init__.py        ← réexporte l'API
├── engine.py          ← le moteur (tout-en-un)
└── fonts/
    └── PressStart2P.ttf   ← police pixel embarquée

Le moteur expose une API globale : on importe from Engine import engine as e et on appelle e.init(), e.cls(), e.btn(), etc.

Sous le capot :

  • App — classe principale qui gère la fenêtre GLFW, l'horloge FPS, le virtual_screen (PIL Image logique), et la boucle d'événements.
  • Graphics — dessin sur l'écran virtuel avec support de caméra, clipping, et alpha. OpenGL est utilisé pour le blit final sur l'écran physique.
  • Input — gestion clavier et souris via GLFW.
  • Resources — chargement d'images.
  • Audiostub (non implémenté, retourne None).

Initialisation

e.init(width, height, title="T.engine", fps=30, display_scale=1, pixel_art=True)

Crée la fenêtre et initialise tous les sous-systèmes.

Paramètre Type Description
width int Largeur logique de l'écran virtuel (ex: 200)
height int Hauteur logique (ex: 200)
title str Titre de la fenêtre
fps int Images par seconde cibles
display_scale int Facteur d'échelle entier (ex: 5 → fenêtre 1000×1000 pour 200×200)
pixel_art bool True = arrondit caméra et coordonnées, ligne 1px logique → display_scale px physiques (défaut: True)

Quand pixel_art=True, la caméra est arrondie aux entiers, les positions de dessin sont arrondies aux entiers, et glLineWidth(display_scale) est utilisé pour les lignes de 1px logique. Mettre à False pour un rendu haute résolution sans ces contraintes.

L'écran virtuel fait width × height pixels. Chaque frame, il est scaled par display_scale et affiché dans la fenêtre système.

e.init(200, 200, "Mon Jeu", 60, 5)

e.run(update, draw)

Lance la boucle de jeu. update() est appelée à chaque frame pour la logique, draw() pour le rendu.

def update():
    pass

def draw():
    e.cls((0, 0, 0))

e.run(update, draw)

e.quit()

Ferme Pygame et termine le processus.

e.width(), e.height()

Retourne la largeur/hauteur logique définies dans init().

e.frame_count()

Retourne le nombre de frames écoulées depuis init().


Graphismes — Graphics

Toutes les fonctions de dessin sont affectées par la caméra (sauf si camera() est réinitialisée).
Les coordonnées sont en pixels logiques.

Primitives de dessin

Fonction Description
cls(color) Remplit tout l'écran virtuel avec une couleur
pset(x, y, color) Dessine un pixel
pget(x, y) → Color Lit la couleur d'un pixel
line(x1, y1, x2, y2, color) Ligne
rect(x, y, w, h, color) Rectangle plein
rectb(x, y, w, h, color) Rectangle vide (1px de bord)
circ(x, y, r, color) Cercle plein
circb(x, y, r, color) Cercle vide (1px de bord)
elli(x, y, w, h, color) Ellipse pleine
ellib(x, y, w, h, color) Ellipse vide (1px de bord)
tri(x1, y1, x2, y2, x3, y3, color) Triangle plein
trib(x1, y1, x2, y2, x3, y3, color) Triangle vide (1px de bord)

Couleurs avec alpha (RGBA)

Toutes les primitives sauf text() et pget() acceptent des couleurs RGBA (4 valeurs).
L'alpha est géré via un blending additif sur une surface temporaire.

# Rectangle semi-transparent
e.rect(10, 10, 50, 50, (255, 0, 0, 128))

Blit d'image

blt(x, y, img, u, v, w, h, colkey=None, rotate=0)
Paramètre Type Description
x, y int Position de destination (coin supérieur gauche)
img PIL.Image ou _Img Image source
u, v int Coordonnées source (coin supérieur gauche dans l'image)
w, h int Dimensions de la région source
colkey Color ou None Couleur de transparence (optionnelle)
rotate float Rotation en degrés horaire (pivot au centre)
e.blt(100, 100, img, 0, 0, 16, 16)          # blit simple
e.blt(100, 100, img, 0, 0, 16, 16, rotate=45)  # rotation

Texte

text(x, y, s, color, font=None)
Paramètre Type Description
x, y int Position
s str Texte à afficher
color tuple Couleur RGB (pas de support alpha)
font FontWrap ou None Police personnalisée (None = police pixel par défaut)

La police par défaut est PressStart2P.ttf taille 6px, rendu sans anti-aliasing pour un aspect pixel art.

e.text(5, 5, "Score: 42", (255, 255, 255))

# Avec une police différente
big = e.default_font(16)
e.text(5, 20, "Titre", (255, 200, 0), font=big)

Caméra

camera(dx, dy)    # Applique un décalage à tout le dessin suivant
camera()          # Réinitialise le décalage à (0, 0)

La caméra est cumulative : tous les appels camera() s'ajoutent.
Pour un système plus avancé, voir la classe Camera.

e.camera(-player.x + 100, -player.y + 100)   # suit le joueur

Clipping

clip(x, y, w, h)    # Active un rectangle de clipping
clip()              # Désactive le clipping

Le clipping est appliqué après la caméra.


Caméra intelligente — Camera

cam = e.Camera(target, screen_width, screen_height,
               mouse_influence=0.2, mouse_limit=10)

Classe réutilisable avec suivi de cible, offset souris, tremblement et flash.

Paramètres du constructeur

Paramètre Défaut Description
target Objet suivi (doit avoir .x, .y)
screen_width Largeur de l'écran logique
screen_height Hauteur de l'écran logique
mouse_influence 0.2 Sensibilité du regard vers la souris (0.0–1.0)
mouse_limit 10 Décalage maximal dû à la souris (en pixels logiques)

Méthodes

Méthode Description
update() Calcule la nouvelle position de la caméra (target + souris + shake)
apply() Applique le décalage au moteur graphique (e.camera(cam.cam_x, cam.cam_y))
shake(duration, intensity) Déclenche un tremblement d'écran
flash(color, alpha, duration) Déclenche un flash d'écran

Attributs

Attribut Description
cam_x, cam_y Position calculée de la caméra (en pixels logiques)
flash_color Couleur du flash actif
flash_alpha Opacité courante du flash
shake_intensity Intensité du tremblement actuel

Le flash doit être dessiné manuellement dans draw() :

if cam.flash_alpha > 0:
    e.camera()
    e.rect(0, 0, e.width(), e.height(), (*cam.flash_color, cam.flash_alpha))

Entrées — Input

Clavier

btn(key)        # → True si la touche est maintenue
btnp(key)       # → True le frame où la touche est pressée (pas de répétition)
btnr(key)       # → True le frame où la touche est relâchée
if e.btn(e.KEY_SPACE):
    player.jump()
if e.btnp(e.KEY_E):
    player.interact()

Souris

mouse_x(), mouse_y()     # → Position logique (divisée par display_scale)
mouse_btn(button)        # → True si le bouton est maintenu
mouse_btnp(button)       # → True le frame du clic
mouse_btnr(button)       # → True le frame du relâchement
mouse(visible=True)      # → Affiche ou cache le curseur système

Boutons : MOUSE_BUTTON_LEFT (1), MOUSE_BUTTON_MIDDLE (2), MOUSE_BUTTON_RIGHT (3)

if e.mouse_btnp(e.MOUSE_BUTTON_LEFT):
    print(f"Clic à ({e.mouse_x()}, {e.mouse_y()})")

Joystick

Non implémenté dans Tiavina.engine.gl (stub).


Ressources — Resources

resources.image(bank, path, colkey=None)   # → PIL.Image ou None
resources.load(filename)                   # → lève NotImplementedError

Images

image() charge une image dans resources.images[bank].

  • bank : index numérique (int)
  • path : chemin relatif ou absolu
  • colkey : couleur de transparence optionnelle (ex: (255, 0, 255))
  • Retourne une PIL.Image (convertie en RGBA)
e.resources.image(0, "./sprites/player.png")
e.resources.image(1, "./sprites/tiles.png", (255, 0, 255))

# Utilisation
img = e.resources.images[1]

Audio — Audio

Non implémenté dans Tiavina.engine.gl. Les méthodes sont des stubs (retournent None sans erreur).


Police pixel — default_font

default_font(size=6, antialias=False)  FontWrap

Charge et met en cache la police PressStart2P.ttf via Pillow (ImageFont.truetype).
L'anti-aliasing est désactivé par défaut pour un rendu pixel art.

La police est stockée dans un cache global _pixel_font_cache : chaque taille n'est chargée qu'une fois.

petite = e.default_font(6)
moyenne = e.default_font(8, antialias=True)   # avec lissage
grande  = e.default_font(16)

Constantes

Touches clavier

KEY_AKEY_Z, KEY_0KEY_9, KEY_SPACE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ESCAPE, KEY_RETURN, KEY_TAB, KEY_BACKSPACE, KEY_LSHIFT, KEY_RSHIFT, KEY_LCTRL, KEY_RCTRL, KEY_LALT, KEY_RALT

Toutes sont des constantes GLFW (glfw.KEY_*) réexportées par le module.

Boutons souris

Constante Valeur
MOUSE_BUTTON_LEFT 1
MOUSE_BUTTON_MIDDLE 2
MOUSE_BUTTON_RIGHT 3

Fonctions globales

Le module engine expose des fonctions globales qui délèguent aux sous-systèmes internes :

Fonction Délègue à
cls(c) graphics.cls(c)
pset(x,y,c) graphics.pset(x,y,c)
pget(x,y) graphics.pget(x,y)
line(x1,y1,x2,y2,c) graphics.line(...)
rect(x,y,w,h,c) graphics.rect(...)
rectb(x,y,w,h,c) graphics.rectb(...)
circ(x,y,r,c) graphics.circ(...)
circb(x,y,r,c) graphics.circb(...)
elli(x,y,w,h,c) graphics.elli(...)
ellib(x,y,w,h,c) graphics.ellib(...)
tri(x1,y1,x2,y2,x3,y3,c) graphics.tri(...)
trib(x1,y1,x2,y2,x3,y3,c) graphics.trib(...)
text(x,y,s,c,font) graphics.text(...)
blt(x,y,img,u,v,w,h,...) graphics.blt(...)
bltm(x,y,tm,u,v,w,h,...) graphics.bltm(...)
clip(x,y,w,h) graphics.clip(...)
camera(x,y) graphics.camera(...)
pal(c1,c2) graphics.pal(...) (stub)
dither(a) graphics.dither(...) (stub)
mouse_btn(b) input.mouse_btn(b)
mouse_btnp(b) input.mouse_btnp(b)
mouse_btnr(b) input.mouse_btnr(b)
btn(k) input.btn(k)
btnp(k) input.btnp(k)
btnr(k) input.btnr(k)

Exemple complet

from Engine import engine as e
from random import randint

# ── Initialisation ─────────────────────────────────────
e.init(200, 200, "Aventurier", fps=60, display_scale=5,
       pixel_art=True)

# ── Ressources ─────────────────────────────────────────
e.resources.image(0, "./sprites/player.png")
e.resources.image(1, "./sprites/tiles.png")

# ── Joueur ─────────────────────────────────────────────
class Player:
    def __init__(self):
        self.x = 100
        self.y = 100
        self.w = 8
        self.h = 8
        self.vie = 100

player = Player()

# ── Caméra ─────────────────────────────────────────────
cam = e.Camera(player, e.width(), e.height(),
               mouse_influence=0.3, mouse_limit=12)

# ── Boucle ─────────────────────────────────────────────
def update():
    # Déplacements
    if e.btn(e.KEY_LEFT):  player.x -= 1
    if e.btn(e.KEY_RIGHT): player.x += 1
    if e.btn(e.KEY_UP):    player.y -= 1
    if e.btn(e.KEY_DOWN):  player.y += 1
    if e.btn(e.KEY_ESCAPE): e.quit()

    # Clic pour shake
    if e.mouse_btnp(e.MOUSE_BUTTON_LEFT):
        cam.shake(10, 6)
        cam.flash((255, 255, 200), 60, 4)

    cam.update()
    cam.apply()

def draw():
    e.cls((30, 30, 40))

    # Sol (en coordonnées monde)
    for i in range(-2, 30):
        for j in range(-2, 20):
            e.rect(i * 8, j * 8, 8, 8, (40, 45, 55))

    # Joueur
    e.rect(player.x, player.y, player.w, player.h,
           (100, 200, 255) if player.vie > 0 else (255, 50, 50))

    # Flash overlay (dessiné avant la réinitialisation caméra)
    if cam.flash_alpha > 0:
        e.camera()
        e.rect(0, 0, e.width(), e.height(),
               (*cam.flash_color, cam.flash_alpha))

    # ── HUD (coordonnées écran) ──
    e.camera()  # ← réinitialise la caméra

    e.text(4, 4, f"Vie: {player.vie}", (255, 255, 255))
    e.text(4, 12, f"Pos: {player.x},{player.y}", (200, 200, 200))

    # Curseur
    e.circb(e.mouse_x(), e.mouse_y(), 3, (255, 255, 255, 80))

e.run(update, draw)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tiavina_engine_gl-0.2.0-py3-none-any.whl (48.1 kB view details)

Uploaded Python 3

File details

Details for the file tiavina_engine_gl-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for tiavina_engine_gl-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 413f0532d9c9e483627ae21e12b1fc863063e1c31a4e01e92e16759e6c5f8656
MD5 0c9dac7d3d7a56ea5d49307d2ee13d31
BLAKE2b-256 58deb8a31476701ed5e28621b28472a640bdaf466f50a1b22b140be1107b7e6b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page