Browse Source

Aggiungi supporto per SDL e sostituisci il sistema grafico basato su Tkinter

master
Matteo Benedetto 1 year ago
parent
commit
0ad9cd47c6
  1. 1
      .env
  2. BIN
      assets/AmaticSC-Regular.ttf
  3. 87
      engine/sdl2.py
  4. 14
      engine/tkinter.py
  5. 24
      rats.py
  6. 78
      sdl2-demo.py
  7. BIN
      units/__pycache__/rat.cpython-313.pyc
  8. 16
      units/rat.py

1
.env

@ -0,0 +1 @@
SDL_VIDEODRIVER=x11

BIN
assets/AmaticSC-Regular.ttf

Binary file not shown.

87
engine/sdl2.py

@ -0,0 +1,87 @@
import sys
import os
import time
import sdl2
import sdl2.ext
from PIL import Image
class GameWindow:
"""Classe che gestisce la finestra di gioco e il rendering grafico."""
def __init__(self, width, height, cell_size, title="Mice!", key_callback=None):
self.cell_size = cell_size
self.width = width * cell_size
self.height = height * cell_size
self.delay = 50
sdl2.ext.init()
self.window = sdl2.ext.Window(title=title, size=(self.width, self.height))
self.window.show()
self.renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED)
self.factory = sdl2.ext.SpriteFactory(renderer=self.renderer)
self.fonts = self.generate_fonts("assets/AmaticSC-Regular.ttf")
self.running = True
def generate_fonts(self,font_file):
fonts = {}
for i in range(10, 70, 1):
fonts.update({i: sdl2.ext.FontManager(font_path=font_file, size=i)})
return fonts
def load_image(self, path, transparent_color=None):
image_path = os.path.join(os.path.dirname(__file__), "..", "assets", path)
image = Image.open(image_path)
if transparent_color:
image = image.convert("RGBA")
datas = image.getdata()
new_data = []
for item in datas:
if item[:3] == transparent_color:
new_data.append((255, 255, 255, 0))
else:
new_data.append(item)
image.putdata(new_data)
# Ridimensiona l'immagine in base a cell_size
scale = self.cell_size // 20
image = image.resize((image.width * scale, image.height * scale), Image.NEAREST)
return self.factory.from_surface(sdl2.ext.pillow_to_surface(image))
def draw_text(self, text, font, position, color):
sprite = self.factory.from_text(text, color=color, fontmanager=font)
if position == "center":
sprite.position = (self.size[0] // 2 - sprite.size[0] // 2, self.size[1] // 2 - sprite.size[1] // 2)
else:
sprite.position = position
self.renderer.copy(sprite, dstrect=sprite.position)
def draw_image(self, x, y, sprite, tag, anchor="nw"):
sprite.position = (x, y)
self.renderer.copy(sprite, dstrect=sprite.position)
def draw_rectangle(self, x, y, width, height, tag, outline="red"):
self.renderer.draw_rect((x, y, width, height), color=sdl2.ext.Color(255, 0, 0))
def delete_tag(self, tag):
pass
def get_image_size(self, image):
return image.size
def update_status(self, text):
self.renderer.fill((10, 3, 100, 40), sdl2.ext.Color(255, 255, 255))
self.draw_text(text, self.fonts[30], (20, 5), sdl2.ext.Color(0, 0, 0))
def new_cycle(self, delay, callback):
pass
def mainloop(self, **kwargs):
while self.running:
self.renderer.clear()
if "bg_update" in kwargs:
kwargs["bg_update"]()
kwargs["update"]()
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
self.running = False
# Disegna qui gli sprite
self.renderer.present()
time.sleep(self.delay / 1000)

14
engine/graphics.py → engine/tkinter.py

@ -3,17 +3,19 @@ import os
class GameWindow:
"""Classe che gestisce la finestra di gioco e il rendering grafico."""
def __init__(self, width, height, cell_size):
def __init__(self, width, height, cell_size, title, key_callback=None):
self.cell_size = cell_size
self.window = tk.Tk()
self.window.title("Mice!")
self.window.title(title)
self.canvas = tk.Canvas(self.window, width=width*cell_size, height=height*cell_size)
self.canvas.pack()
self.menu = tk.Menu(self.window)
self.menu.add_command(label="Quit", command=self.window.destroy)
self.status_bar = tk.Label(self.window, text="Welcome to Mice!", bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar = tk.Label(self.window, text=title, bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
self.window.config(menu=self.menu)
if key_callback:
self.window.bind("<Key>", key_callback)
def load_image(self, path, transparent_color=None):
image = tk.PhotoImage(file=os.path.join(os.path.dirname(__file__), "..", "assets", path))
@ -46,5 +48,9 @@ class GameWindow:
def new_cycle(self, delay, callback):
self.window.after(delay, callback)
def mainloop(self):
def mainloop(self, **kwargs):
kwargs["update"]()
self.window.mainloop()
def get_image_size(self, image):
return image.width(), image.height()

24
rats.py

@ -1,20 +1,18 @@
import json
import tkinter as tk
import random
from units import rat
import uuid
import subprocess
from engine import maze, graphics
from engine import maze, sdl2 as engine
class MiceMaze:
def __init__(self, maze_file):
self.map = maze.Map(maze_file)
self.audio = True
self.cell_size = 60
self.engine = graphics.GameWindow(self.map.width, self.map.height, self.cell_size)
self.engine = engine.GameWindow(self.map.width, self.map.height, self.cell_size, "Mice!", key_callback=self.key_pressed)
self.graphics_load()
self.engine.bind("<KeyPress>", self.key_pressed)
self.units = {}
for _ in range(5):
self.new_rat()
def new_rat(self, position=None):
@ -34,11 +32,11 @@ class MiceMaze:
return random.choice(self._valid_positions)
def draw_maze(self):
for y in range(self.map.height):
for x in range(self.map.width):
variant = random.randint(0, 3)
tile = self.grasses[variant] if self.map.matrix[y][x] else self.tunnel
self.engine.draw_image(x*self.cell_size, y*self.cell_size, tile, tag="maze", anchor="nw")
for y, row in enumerate(self.map.matrix):
for x, cell in enumerate(row):
variant = x*y % 4
tile = self.grasses[variant] if cell else self.tunnel
self.engine.draw_image(x * self.cell_size, y * self.cell_size, tile, tag="maze")
def update_maze(self):
self.engine.delete_tag("unit")
@ -46,13 +44,13 @@ class MiceMaze:
unit.move()
unit.collisions()
unit.draw()
self.engine.update_status(f"Units: {len(self.units)}")
self.engine.update_status(f"Mice: {len(self.units)}")
self.engine.new_cycle(50, self.update_maze)
def run(self):
self.draw_maze()
self.update_maze()
self.engine.mainloop()
self.engine.mainloop(update=self.update_maze, bg_update=self.draw_maze)
def key_pressed(self, event):
if event.keysym == "q":

78
sdl2-demo.py

@ -0,0 +1,78 @@
import sys
import sdl2
from PIL import Image
import sdl2.sdlttf
import sdl2.ext
class Main:
def __init__(self):
sdl2.ext.init()
self.size = (640, 480)
self.window = sdl2.ext.Window("PySDL2 Demo", size=self.size)
self.window.show()
self.renderer = sdl2.ext.Renderer(self.window)
self.factory = sdl2.ext.SpriteFactory(renderer=self.renderer)
self.fonts = self.generate_fonts("assets/AmaticSC-Regular.ttf")
self.running = True
self.assets = self.graphics_load()
def generate_fonts(self,font_file):
fonts = {}
for i in range(10, 70, 1):
fonts.update({i: sdl2.ext.FontManager(font_path=font_file, size=i)})
return fonts
def draw_text(self, text, font, position, color):
sprite = self.factory.from_text(text, fontmanager=font, color=color)
if position == "center":
sprite.position = (self.size[0] // 2 - sprite.size[0] // 2, self.size[1] // 2 - sprite.size[1] // 2)
else:
sprite.position = position
self.renderer.copy(sprite, dstrect=sprite.position)
def draw_image(self, sprite, position):
sprite.position = position
self.renderer.copy(sprite, dstrect=sprite.position)
def load_image(self, path, transparent_color=None):
image = Image.open(path)
image = image.resize((image.width * 3, image.height * 3), Image.NEAREST)
if transparent_color:
image = image.convert("RGBA")
datas = image.getdata()
new_data = []
for item in datas:
if item[:3] == transparent_color:
new_data.append((255, 255, 255, 0))
else:
new_data.append(item)
image.putdata(new_data)
return self.factory.from_surface(sdl2.ext.pillow_to_surface(image))
def run(self):
while self.running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
self.running = False
break
self.renderer.clear()
self.draw_text("MatGames Corp.", self.fonts[50], (0, 0), sdl2.ext.Color(255, 255, 255))
self.draw_image(self.assets["MALE"]["DOWN"], (100, 100))
self.renderer.present()
sdl2.ext.quit()
return 0
def graphics_load(self):
rat_assets = {}
for sex in ["MALE", "FEMALE", "BABY"]:
rat_assets[sex] = {}
for direction in ["UP", "DOWN", "LEFT", "RIGHT"]:
rat_assets[sex][direction] = self.load_image(f"assets/Rat/BMP_{sex}_{direction}.png", transparent_color=(128, 128, 128))
return rat_assets
if __name__ == "__main__":
sys.exit(Main().run())

BIN
units/__pycache__/rat.cpython-313.pyc

Binary file not shown.

16
units/rat.py

@ -1,7 +1,7 @@
from .unit import Unit
import random
import uuid
from engine.graphics import GameWindow
from engine.tkinter import GameWindow
# Costanti
AGE_THRESHOLD = 200
@ -22,6 +22,7 @@ class Rat(Unit):
self.speed = .10
self.partial_move = 0
self.position_before = position
self.fight = True
def calculate_rat_direction(self):
x, y = self.position
@ -87,9 +88,9 @@ class Rat(Unit):
y2 > oy1 + OVERLAP_TOLERANCE):
continue
if self.id in self.game.units and unit.id in self.game.units:
if self.sex == unit.sex:
if self.sex == unit.sex and self.fight:
self.die(unit)
else:
elif self.sex != unit.sex:
if "fuck" in dir(self):
self.fuck(unit)
@ -104,6 +105,7 @@ class Rat(Unit):
direction = self.calculate_rat_direction()
sex = self.sex if self.age > AGE_THRESHOLD else "BABY"
image = self.game.rat_assets[sex][direction]
image_size = self.game.engine.get_image_size(image)
self.rat_image = image
partial_x, partial_y = 0, 0
@ -112,11 +114,11 @@ class Rat(Unit):
else:
partial_x = self.partial_move * self.game.cell_size * (1 if direction == "RIGHT" else -1)
x_pos = self.position_before[0] * self.game.cell_size + (self.game.cell_size - image.width()) // 2 + partial_x
y_pos = self.position_before[1] * self.game.cell_size + (self.game.cell_size - image.height()) // 2 + partial_y
x_pos = self.position_before[0] * self.game.cell_size + (self.game.cell_size - image_size[0]) // 2 + partial_x
y_pos = self.position_before[1] * self.game.cell_size + (self.game.cell_size - image_size[1]) // 2 + partial_y
self.game.engine.draw_image(x_pos, y_pos, image=image, anchor="nw", tag="unit")
self.bbox = (x_pos, y_pos, x_pos + image.width(), y_pos + image.height())
self.game.engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit")
self.bbox = (x_pos, y_pos, x_pos + image_size[0], y_pos + image_size[1])
self.game.engine.draw_rectangle(self.bbox[0], self.bbox[1], self.bbox[2] - self.bbox[0], self.bbox[3] - self.bbox[1], "unit")
class Male(Rat):

Loading…
Cancel
Save