Browse Source

Aggiungi classi per la gestione delle entità e dei tile, con supporto per il caricamento di texture e coordinate isometriche

master
Matteo Benedetto 10 months ago
parent
commit
668e488280
  1. 0
      enne2engine/Entities/__init__.py
  2. 22
      enne2engine/Entities/entity.py
  3. 0
      enne2engine/Entities/unit.py
  4. 9
      enne2engine/controls.py
  5. 65
      enne2engine/engine_demo.py
  6. 7
      enne2engine/isogeometry.py
  7. 104
      enne2engine/sdl2_wrapper.py
  8. 18
      enne2engine/spritemanager.py
  9. 8
      enne2engine/tilemanager.py

0
enne2engine/Entities/__init__.py

22
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)

0
enne2engine/Entities/unit.py

9
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.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.create_background(self.map, "tiles")
def scroll_right(self):
self.graphics.view_offset_x -= 10
self.graphics.create_background(self.map, "tiles")

65
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,27 +42,36 @@ 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)
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:
variation = random.choice(["067", "083", "075", "081"])
self.graphics.render_tile("tiles", f"landscapeTiles_{variation}", x, y)
return 0.8
def load_assets(self):
self.graphics.load_tilesheet("tiles", "assets/tiles/landscapeTiles_sheet.png")
@ -70,5 +84,4 @@ class GameEngine(UserControls):
if __name__ == "__main__":
engine = GameEngine()
engine.load_assets()
engine.run()

7
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
@ -82,9 +83,9 @@ class IsometricGeometry:
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

104
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

18
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)

8
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):
@ -28,3 +31,6 @@ class TileManager:
def get_tilesheet_surface(self):
return self.tilesheet_surface
def get_tilesheet_texture(self):
return self.tilesheet_texture
Loading…
Cancel
Save