diff --git a/.env b/.env new file mode 100644 index 0000000..e3b97ec --- /dev/null +++ b/.env @@ -0,0 +1 @@ +SDL_VIDEODRIVER=x11 \ No newline at end of file diff --git a/assets/AmaticSC-Regular.ttf b/assets/AmaticSC-Regular.ttf new file mode 100644 index 0000000..90b86df Binary files /dev/null and b/assets/AmaticSC-Regular.ttf differ diff --git a/engine/sdl2.py b/engine/sdl2.py new file mode 100644 index 0000000..497ed58 --- /dev/null +++ b/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) diff --git a/engine/graphics.py b/engine/tkinter.py similarity index 79% rename from engine/graphics.py rename to engine/tkinter.py index 641cfca..49643be 100644 --- a/engine/graphics.py +++ b/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_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): - self.window.mainloop() \ No newline at end of file + def mainloop(self, **kwargs): + kwargs["update"]() + self.window.mainloop() + + def get_image_size(self, image): + return image.width(), image.height() \ No newline at end of file diff --git a/rats.py b/rats.py index 9b4077f..da9969d 100644 --- a/rats.py +++ b/rats.py @@ -1,21 +1,19 @@ -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("", self.key_pressed) self.units = {} - self.new_rat() + for _ in range(5): + self.new_rat() def new_rat(self, position=None): if position is 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": diff --git a/sdl2-demo.py b/sdl2-demo.py new file mode 100644 index 0000000..adce670 --- /dev/null +++ b/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()) \ No newline at end of file diff --git a/units/__pycache__/rat.cpython-313.pyc b/units/__pycache__/rat.cpython-313.pyc index 9f07f18..ba90b4b 100644 Binary files a/units/__pycache__/rat.cpython-313.pyc and b/units/__pycache__/rat.cpython-313.pyc differ diff --git a/units/rat.py b/units/rat.py index dc18f8c..67c1e4a 100644 --- a/units/rat.py +++ b/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):