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. 26
      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: class GameWindow:
"""Classe che gestisce la finestra di gioco e il rendering grafico.""" """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.cell_size = cell_size
self.window = tk.Tk() 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 = tk.Canvas(self.window, width=width*cell_size, height=height*cell_size)
self.canvas.pack() self.canvas.pack()
self.menu = tk.Menu(self.window) self.menu = tk.Menu(self.window)
self.menu.add_command(label="Quit", command=self.window.destroy) 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.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
self.window.config(menu=self.menu) self.window.config(menu=self.menu)
if key_callback:
self.window.bind("<Key>", key_callback)
def load_image(self, path, transparent_color=None): def load_image(self, path, transparent_color=None):
image = tk.PhotoImage(file=os.path.join(os.path.dirname(__file__), "..", "assets", path)) 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): def new_cycle(self, delay, callback):
self.window.after(delay, callback) self.window.after(delay, callback)
def mainloop(self): def mainloop(self, **kwargs):
kwargs["update"]()
self.window.mainloop() self.window.mainloop()
def get_image_size(self, image):
return image.width(), image.height()

26
rats.py

@ -1,21 +1,19 @@
import json
import tkinter as tk
import random import random
from units import rat from units import rat
import uuid import uuid
import subprocess import subprocess
from engine import maze, graphics from engine import maze, sdl2 as engine
class MiceMaze: class MiceMaze:
def __init__(self, maze_file): def __init__(self, maze_file):
self.map = maze.Map(maze_file) self.map = maze.Map(maze_file)
self.audio = True self.audio = True
self.cell_size = 60 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.graphics_load()
self.engine.bind("<KeyPress>", self.key_pressed)
self.units = {} self.units = {}
self.new_rat() for _ in range(5):
self.new_rat()
def new_rat(self, position=None): def new_rat(self, position=None):
if position is None: if position is None:
@ -34,11 +32,11 @@ class MiceMaze:
return random.choice(self._valid_positions) return random.choice(self._valid_positions)
def draw_maze(self): def draw_maze(self):
for y in range(self.map.height): for y, row in enumerate(self.map.matrix):
for x in range(self.map.width): for x, cell in enumerate(row):
variant = random.randint(0, 3) variant = x*y % 4
tile = self.grasses[variant] if self.map.matrix[y][x] else self.tunnel tile = self.grasses[variant] if cell else self.tunnel
self.engine.draw_image(x*self.cell_size, y*self.cell_size, tile, tag="maze", anchor="nw") self.engine.draw_image(x * self.cell_size, y * self.cell_size, tile, tag="maze")
def update_maze(self): def update_maze(self):
self.engine.delete_tag("unit") self.engine.delete_tag("unit")
@ -46,13 +44,13 @@ class MiceMaze:
unit.move() unit.move()
unit.collisions() unit.collisions()
unit.draw() 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) self.engine.new_cycle(50, self.update_maze)
def run(self): def run(self):
self.draw_maze() self.draw_maze()
self.update_maze() self.engine.mainloop(update=self.update_maze, bg_update=self.draw_maze)
self.engine.mainloop()
def key_pressed(self, event): def key_pressed(self, event):
if event.keysym == "q": 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 from .unit import Unit
import random import random
import uuid import uuid
from engine.graphics import GameWindow from engine.tkinter import GameWindow
# Costanti # Costanti
AGE_THRESHOLD = 200 AGE_THRESHOLD = 200
@ -22,6 +22,7 @@ class Rat(Unit):
self.speed = .10 self.speed = .10
self.partial_move = 0 self.partial_move = 0
self.position_before = position self.position_before = position
self.fight = True
def calculate_rat_direction(self): def calculate_rat_direction(self):
x, y = self.position x, y = self.position
@ -87,9 +88,9 @@ class Rat(Unit):
y2 > oy1 + OVERLAP_TOLERANCE): y2 > oy1 + OVERLAP_TOLERANCE):
continue continue
if self.id in self.game.units and unit.id in self.game.units: 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) self.die(unit)
else: elif self.sex != unit.sex:
if "fuck" in dir(self): if "fuck" in dir(self):
self.fuck(unit) self.fuck(unit)
@ -104,6 +105,7 @@ class Rat(Unit):
direction = self.calculate_rat_direction() direction = self.calculate_rat_direction()
sex = self.sex if self.age > AGE_THRESHOLD else "BABY" sex = self.sex if self.age > AGE_THRESHOLD else "BABY"
image = self.game.rat_assets[sex][direction] image = self.game.rat_assets[sex][direction]
image_size = self.game.engine.get_image_size(image)
self.rat_image = image self.rat_image = image
partial_x, partial_y = 0, 0 partial_x, partial_y = 0, 0
@ -112,11 +114,11 @@ class Rat(Unit):
else: else:
partial_x = self.partial_move * self.game.cell_size * (1 if direction == "RIGHT" else -1) 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 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.height()) // 2 + partial_y 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.game.engine.draw_image(x_pos, y_pos, image, anchor="nw", tag="unit")
self.bbox = (x_pos, y_pos, x_pos + image.width(), y_pos + image.height()) 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") 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): class Male(Rat):

Loading…
Cancel
Save