diff --git a/enne2engine/Entities/__init__.py b/enne2engine/Entities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/enne2engine/Entities/entity.py b/enne2engine/Entities/entity.py new file mode 100644 index 0000000..f96d3e9 --- /dev/null +++ b/enne2engine/Entities/entity.py @@ -0,0 +1,22 @@ +class Entity: + def __init__(self, asset, x, y, action, direction, speed, engine): + self.asset = asset + + self.x = x + self.y = y + self.action = action + self.direction = direction + self.speed = speed + self.frame = 0 + self.graphics = engine.graphics + self.engine = engine + + def update(self): + x, y = self.graphics.iso_transform(self.x, self.y) + occlusion = self.graphics.get_distance((self.x, self.y), self.engine.cursor_pos) / 4 + if occlusion >= 0.8: + return + + self.frame = self.graphics.render_sprite(f"{self.asset}_{self.action}_dir{self.direction}", x,y, self.frame, occlusion) + + \ No newline at end of file diff --git a/enne2engine/Entities/unit.py b/enne2engine/Entities/unit.py new file mode 100644 index 0000000..e69de29 diff --git a/enne2engine/controls.py b/enne2engine/controls.py index f70ae61..5cebbbd 100644 --- a/enne2engine/controls.py +++ b/enne2engine/controls.py @@ -14,20 +14,25 @@ class UserControls: configs[file[:-5]] = json.load(f) return configs def handle_events(self, mapping, event): - print(self.configs[mapping].get(event)) + if event.startswith("MOUSEMOTION"): + x, y = event.split(":")[1:] + self.set_cursor(int(x), int(y)) if method := self.configs[mapping].get(event): getattr(self, method)() def scroll_up(self): - if self.graphics.view_offset_y < 0: - self.graphics.view_offset_y += 10 + self.graphics.view_offset_y += 10 + self.graphics.create_background(self.map, "tiles") def scroll_down(self): self.graphics.view_offset_y -= 10 + self.graphics.create_background(self.map, "tiles") def scroll_left(self): - self.graphics.view_offset_x += 10 + self.graphics.view_offset_x += 10 + self.graphics.create_background(self.map, "tiles") def scroll_right(self): - self.graphics.view_offset_x -= 10 \ No newline at end of file + self.graphics.view_offset_x -= 10 + self.graphics.create_background(self.map, "tiles") \ No newline at end of file diff --git a/enne2engine/engine_demo.py b/enne2engine/engine_demo.py index bc5bbb3..d8aafb6 100644 --- a/enne2engine/engine_demo.py +++ b/enne2engine/engine_demo.py @@ -2,9 +2,10 @@ from sdl2_wrapper import SDL2Wrapper from pyglet_wrapper import PygletWrapper from controls import UserControls import sys -import random import os +from Entities.entity import Entity + class GameEngine(UserControls): def __init__(self): super().__init__() @@ -12,23 +13,27 @@ class GameEngine(UserControls): self.graphics = PygletWrapper() else: self.graphics = SDL2Wrapper() - - self.map = [[1, 1, 0, 0, 1,0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]] + self.map = [ + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], + [{ 'wall': True, 'tile': "landscapeTiles_066" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }], + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], + [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }] + ] self.frame_time = 0 + self.cursor_pos = (0, 0) + + self.load_assets() + self.entities = [] def run(self): running = True self.graphics.create_background(self.map, "tiles") - next = 0 + self.entities.append(Entity("knight", 0, 0, "idle", 1, 1, self)) + while running: perf_counter = self.graphics.get_perf_counter() event = self.graphics.handle_events() @@ -37,28 +42,37 @@ class GameEngine(UserControls): running = False if event == "QUIT" else True self.graphics.clear_screen() self.graphics.render_background() - pos = 0 - for animation in ["walk"]: - n = self.graphics.render_sprite(f"knight_{animation}_dir1", - 100+pos, 100, next) - pos += 100 - next += 1 + self.cursor_pos = self.graphics.draw_cursor() + for entity in self.entities: + entity.update() self.graphics.update_status(f"Frame time: {round(self.frame_time)}ms - FPS: {round(1000/self.frame_time if self.frame_time != 0 else 1)}") self.graphics.present_renderer() self.frame_time = self.graphics.get_frame_time(perf_counter) self.graphics.delay_frame(self.frame_time,50) - #self.frame_time = self.graphics.get_frame_time(perf_counter) self.graphics.quit() - + def set_cursor(self, x, y): + self.graphics.cursor = (x, y) + def render_background(self): for y, row in enumerate(self.map): - for x, tile_name in enumerate(row): - if self.map[y][x] == 1: - self.graphics.render_tile("tiles", "landscapeTiles_066", x, y) - else: - variation = random.choice(["067", "083", "075", "081"]) - self.graphics.render_tile("tiles", f"landscapeTiles_{variation}", x, y) - + for x, cell in enumerate(row): + cell["occlusion"] = self.get_occlusion(x, y) + if cell['occlusion']==1: + continue + self.graphics.render_tile("tiles", cell, x, y) + + def get_occlusion(self, x, y): + if (x, y) == self.cursor_pos: + return 0 + distance = self.graphics.get_distance((x, y), self.cursor_pos) + if distance <2: + return 0.2 + elif distance < 3: + return 0.5 + else: + return 0.8 + + def load_assets(self): self.graphics.load_tilesheet("tiles", "assets/tiles/landscapeTiles_sheet.png") for dir in os.listdir("assets/KnightBasic"): @@ -70,5 +84,4 @@ class GameEngine(UserControls): if __name__ == "__main__": engine = GameEngine() - engine.load_assets() engine.run() \ No newline at end of file diff --git a/enne2engine/isogeometry.py b/enne2engine/isogeometry.py index df5fff5..97eede9 100644 --- a/enne2engine/isogeometry.py +++ b/enne2engine/isogeometry.py @@ -1,5 +1,6 @@ from collections import deque class IsometricGeometry: + CELL_PADDING_X = 1 def calculate_coordinates(self, event): if self.input_lock: return @@ -78,13 +79,13 @@ class IsometricGeometry: def iso_transform(self, x, y): screen_x = (x - y) * self.cell_size // 2 + self.view_offset_x - screen_y = (x + y) * self.cell_size // 4 + self.view_offset_y + screen_y = (x + y) * self.cell_size // 4 + self.view_offset_y return screen_x, screen_y def inv_iso_transform(self, screen_x, screen_y): - x = ((screen_x - self.view_offset_x) / (self.cell_width / 2) + (screen_y - self.view_offset_y) / (self.cell_height / 2)) / 2 - y = ((screen_y - self.view_offset_y) / (self.cell_height / 2) - (screen_x - self.view_offset_x) / (self.cell_width / 2)) / 2 - return int(x), int(y) + x = (2 * screen_y + screen_x - 2 * self.view_offset_x) // self.cell_size + y = (2 * screen_y - screen_x + 2 * self.view_offset_x) // self.cell_size + return x, y def get_distance(self, position1, position2): x1, y1 = position1 diff --git a/enne2engine/sdl2_wrapper.py b/enne2engine/sdl2_wrapper.py index 05e1c47..5ab23fa 100644 --- a/enne2engine/sdl2_wrapper.py +++ b/enne2engine/sdl2_wrapper.py @@ -1,5 +1,6 @@ import random import math +from math import sqrt import ctypes import sdl2 import sdl2.ext @@ -14,8 +15,8 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): self.view_size = (800, 600) self.target_size = (800, 600) self.cell_size = 132 - self.view_offset_x = 0 - self.view_offset_y = 0 + self.view_offset_x = 400 + self.view_offset_y = 200 self.surface_width = 0 self.surface_height = 0 self.window = sdl2.ext.Window("My Game", size=self.view_size) @@ -25,6 +26,7 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): self.sprite_managers = {} self.factory = sdl2.ext.SpriteFactory(renderer=self.renderer) self.fonts = self.generate_fonts("assets/decterm.ttf") + self.cursor = (0, 0) def get_perf_counter(self): return int(sdl2.SDL_GetPerformanceCounter()) @@ -39,6 +41,9 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): elif event.type == sdl2.SDL_KEYDOWN: key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8') return f"KEYDOWN:{key}".lower() + elif event.type == sdl2.SDL_MOUSEMOTION: + x, y = event.motion.x, event.motion.y + return f"MOUSEMOTION:{x}:{y}" return False def clear_screen(self, color=(0, 0, 0, 255)): # Aggiunto valore alfa di default @@ -54,26 +59,55 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): def load_spritesheet(self, name, path): surface = sdl2.ext.load_image(path.replace('.json', '.png')) texture = sdl2.ext.Texture(self.renderer, surface) - self.sprite_managers[name] = SpriteManager(path, surface, texture) + self.sprite_managers[name] = SpriteManager(path, surface, texture, self.cell_size) def load_tilesheet(self, name, path): surface = sdl2.ext.load_image(path) - self.tile_managers[name] = TileManager(path.replace('.png', '.xml'), surface) + texture = sdl2.ext.Texture(self.renderer, surface) + + self.tile_managers[name] = TileManager(path.replace('.png', '.xml'), surface, texture, self.cell_size) def load_sprite(self, spritesheet, name): return self.tile_managers[spritesheet].get_tile_rect(name) - def render_sprite(self, name, x, y, frame): + def render_sprite(self, name, x, y, frame, occlusion=0): srcrect, total_frames = self.sprite_managers[name].get_frame_rect(frame) - + y -= self.cell_size // 2 + if occlusion: + original_color_mods_r_g_b_a = self.apply_texture_color_mdod(self.sprite_managers[name].spritesheet_texture, occlusion) self.renderer.copy(self.sprite_managers[name].spritesheet_texture, - dstrect=(x, y, srcrect[2], srcrect[3]), srcrect=srcrect) + dstrect=(x, y, self.cell_size, self.cell_size), + srcrect=srcrect) + if occlusion: + sdl2.SDL_SetTextureColorMod(self.sprite_managers[name].spritesheet_texture.tx, + *original_color_mods_r_g_b_a) return (frame + 1) % total_frames + def render_tile(self, spritesheet_name, cell, x, y): + tile_name = cell.get('tile') + occlusion = cell.get('occlusion', 0) + texture = self.tile_managers[spritesheet_name].get_tilesheet_texture() + tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile_name) + + if tile_rect is not None: + iso_x, iso_y = self.iso_transform(x, y) + y_offset = self.cell_size // 2 - tile_rect[3] + dst_rect = (iso_x, iso_y + y_offset, + tile_rect[2], tile_rect[3]) + + # Salva i valori originali dei moduli di colore + original_color_mods_r_g_b_a = self.apply_texture_color_mdod(texture, occlusion) + # Copia la texture con il nuovo modulo di colore + self.renderer.copy(self.tile_managers[spritesheet_name].get_tilesheet_texture(), + sdl2.SDL_Rect(*tile_rect), + sdl2.SDL_Rect(*dst_rect)) + + sdl2.SDL_SetTextureColorMod(texture.tx, *original_color_mods_r_g_b_a) + def create_background(self, map, spritesheet_name): - self.surface_width = round(self.cell_size * len(map[0]) * math.sqrt(2)) - self.surface_height = round(self.cell_size * len(map) * math.sqrt(2) / 2) + self.surface_width = round(self.cell_size * len(map[0])*sqrt(2)) + self.surface_height = round(self.cell_size//2 * len(map)*sqrt(2)) bg_surface = sdl2.SDL_CreateRGBSurface(0, self.surface_width, self.surface_height, 32, 0, 0, 0, 0) def blit_tile(tile, x, y): @@ -81,9 +115,10 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): if tile_rect is not None: vertical_offset = self.cell_size - tile_rect[3] horizontal_offset = (self.cell_size - tile_rect[2]) - x, y = self.iso_transform(x, y) - dst_rect = (x + horizontal_offset- self.cell_size//2 + self.surface_width // 2, - y + vertical_offset, + iso_x, iso_y = self.iso_transform(x, y) + iso_y += vertical_offset + iso_x += horizontal_offset + dst_rect = (iso_x, iso_y, tile_rect[2], tile_rect[3]) sdl2.SDL_BlitSurface(self.tile_managers[spritesheet_name].get_tilesheet_surface(), sdl2.SDL_Rect(*tile_rect), @@ -92,18 +127,15 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): #sdl2.SDL_FillRect(bg_surface, sdl2.SDL_Rect(*dst_rect), sdl2.SDL_MapRGB(bg_surface.contents.format, 0, 0, 255)) for y, row in enumerate(map): - for x, tile_name in enumerate(row): - if map[y][x] == 1: - blit_tile("landscapeTiles_066", x, y) - else: - variation = random.choice(["067", "083", "075", "081"]) - blit_tile(f"landscapeTiles_{variation}", x, y) + for x, cell in enumerate(row): + tile_name = cell.get('tile') + blit_tile(tile_name, x, y) self.background_texture = self.factory.from_surface(bg_surface) def render_background(self): self.renderer.copy(self.background_texture, - dstrect=(self.view_offset_x, self.view_offset_y, self.surface_width, self.surface_height), + dstrect=(0, 0, self.surface_width, self.surface_height), srcrect=(0,0, self.surface_width, self.surface_height)) def get_frame_time(self, perf_counter): @@ -116,3 +148,37 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): else: delay = 0 sdl2.SDL_Delay(delay) + + def draw_cursor(self): + x, y = self.cursor + x += self.view_offset_x - self.cell_size // 2 + y -= self.view_offset_y - self.cell_size // 4 + c_x, c_y = self.inv_iso_transform(x, y) + print(c_x, c_y) + iso_x, iso_y = self.iso_transform(c_x, c_y) + self.renderer.draw_line(points=[(iso_x, iso_y), (iso_x + self.cell_size//2, iso_y + self.cell_size//4)], color=(255, 0, 0, 255)) + self.renderer.draw_line(points=[(iso_x + self.cell_size//2, iso_y + self.cell_size//4), (iso_x + self.cell_size, iso_y)], color=(255, 0, 0, 255)) + self.renderer.draw_line(points=[(iso_x + self.cell_size, iso_y), (iso_x + self.cell_size//2, iso_y - self.cell_size//4)], color=(255, 0, 0, 255)) + self.renderer.draw_line(points=[(iso_x + self.cell_size//2, iso_y - self.cell_size//4), (iso_x, iso_y)], color=(255, 0, 0, 255)) + return c_x, c_y + + def apply_texture_color_mdod(self, texture, occlusion): + color_mod_r = int(255 * (1 - occlusion)) + color_mod_g = int(255 * (1 - occlusion)) + color_mod_b = int(255 * (1 - occlusion)) + original_color_mods_r_g_b_a = [0] * 3 + r_ptr = ctypes.pointer(ctypes.c_ubyte()) + g_ptr = ctypes.pointer(ctypes.c_ubyte()) + b_ptr = ctypes.pointer(ctypes.c_ubyte()) + sdl2.SDL_GetTextureColorMod(texture.tx, + r_ptr, + g_ptr, + b_ptr) + original_color_mods_r_g_b_a[0] = r_ptr.contents.value + original_color_mods_r_g_b_a[1] = g_ptr.contents.value + original_color_mods_r_g_b_a[2] = b_ptr.contents.value + sdl2.SDL_SetTextureColorMod(texture.tx, + color_mod_r, + color_mod_g, + color_mod_b) + return original_color_mods_r_g_b_a \ No newline at end of file diff --git a/enne2engine/spritemanager.py b/enne2engine/spritemanager.py index cdb66bb..409e1e6 100644 --- a/enne2engine/spritemanager.py +++ b/enne2engine/spritemanager.py @@ -1,19 +1,27 @@ import json class SpriteManager: - def __init__(self, path, surface, texture): + def __init__(self, path, surface, texture, cell_size): self.spritesheet_texture = texture self.spritesheet_surface = surface + self.cell_size = cell_size self.frames_rect = [] self.load_spritemap(path) def load_spritemap(self, json_path): with open(json_path) as f: for frame in json.load(f).get("frames"): - self.frames_rect.append((frame["frame"]["x"], - frame["frame"]["y"], - frame["frame"]["w"], - frame["frame"]["h"])) + x = frame["frame"]["x"] + y = frame["frame"]["y"] + w = frame["frame"]["w"] + h = frame["frame"]["h"] + + # Calcola le coordinate per un quadrato centrato + quad_x = x + (w - self.cell_size) // 2 + quad_y = y + (h - self.cell_size) // 2 + + # Aggiungi il rettangolo del quadrato al centro + self.frames_rect.append((quad_x, quad_y, self.cell_size, self.cell_size)) def get_frame_rect(self, frame): return self.frames_rect[frame % len(self.frames_rect)], len(self.frames_rect) \ No newline at end of file diff --git a/enne2engine/tilemanager.py b/enne2engine/tilemanager.py index 070d881..6f6c13e 100644 --- a/enne2engine/tilemanager.py +++ b/enne2engine/tilemanager.py @@ -1,9 +1,11 @@ import xml.etree.ElementTree as ET class TileManager: - def __init__(self, path, surface): + def __init__(self, path, surface, texture, cell_size): + self.tilesheet_texture = texture self.tile_positions = {} self.tilesheet_surface = surface + self.cell_size = cell_size self.load_tilemap(path) def load_tilemap(self, xml_path): @@ -16,6 +18,7 @@ class TileManager: y = int(tile.get('y')) width = int(tile.get('width')) height = int(tile.get('height')) + self.tile_positions[name] = (x, y, width, height) def get_tile_rect(self, tile_name): @@ -27,4 +30,7 @@ class TileManager: return self.tile_positions.keys() def get_tilesheet_surface(self): - return self.tilesheet_surface \ No newline at end of file + return self.tilesheet_surface + + def get_tilesheet_texture(self): + return self.tilesheet_texture \ No newline at end of file