MarkelZ / pygame-light2d

Fast 2D dynamic lighting engine for Pygame.
MIT License
20 stars 1 forks source link

Suggestion: implementing pygame.Surface methods #6

Closed RomainPastureau closed 2 months ago

RomainPastureau commented 3 months ago

Hi! I am really enjoying your work so far :)

I am currently in the process of implement the lighting system in my 2D game - it was already quite advanced so I had to modify quite a lot of things. That led me to think there are a few things that could slightly improve the experience, i'm leaving them here :)

1. render_texture parameters

The behavior of the render_texture's parameters should mimic the one from the Pygame blit method. dest should accept tuples on top of Rect (maybe adding a type assertion in the function, and turning the tuple into pygame.Rect(dest[0], dest[1], 0, 0); in the same idea, the parameter area should accept None (in that case, the whole sprite area would be selected, just as for blit). That way, blit methods could almost seamlessly be turned into render_texture methods. In other words, right now I have to turn: window.blit(sprite, (0, 0)) into light_engine.render_texture(sprite, pl2d.BACKGROUND, pygame.Rect(0, 0, sprite.width, sprite.height), pygame.Rect(0, 0, sprite.width, sprite.height) while a easier way would be light_engine.render_texture(sprite, pl2d.BACKGROUND, (0, 0))

2. Blitting on a Surface

In my project, I have a function allowing me to create character surfaces holding an item by blitting the item on top of the character surface: for example, I am adding a torch on top of my character "holding an item" sprites. I do it via:

character_holding = pygame.image.load("character_holding.png")
torch = pygame.image.load("torch.png")
character_holding_torch = character_holding.blit(torch, (0, 0))

Right now it doesn't seem possible to render a texture on top of another texture, so I have to do the pygame blit, before using the surface_to_texture method. Do you think it would be possible to render directly on textures in the future?

3. Allow for resizable windows

I wanted my game to be adaptable for any resolution, so I made a function that reacts to a window resize to re-render all of the loaded sprites at the correct resolution. This implies that the window was created with the pygame.RESIZABLE flag, which doesn't seem to be accepted by the LightingEngine function. I went and tried to add it manually in your code, and it seems to work. Could you add this option as a parameter?

Thank you again for all your work :)

MarkelZ commented 3 months ago

Hey @RomainPastureau this is awesome, very glad you like my project! I'm not sure what to do since I am considering reimplementing the whole engine using my shader package for pygame. Nevertheless, I will be implementing your suggestions soon, during summer. Thank you :)

RomainPastureau commented 3 months ago

Glad it helps, and cannot wait to see the result!

tigercoding56 commented 2 months ago

shader package for pygame.

you have a shader package for pygame :D , i did not know about that , so maybe add a sneaky mention in readme.md?

MarkelZ commented 2 months ago

Good idea @tigercoding56, this engine isn't compatible with the shader package for now, but I'll add a mention in the readme once I reimplement it

MarkelZ commented 2 months ago

Hi! I rewrote the lighting engine using my shader package, you can install it with pip3 install pygame-light2d>=2.1.1.

1. render_texture parameters

The engine now has a method for rendering sprites with transformations:

light_engine.render_transformed(sprite, pl2d.BACKGROUND, position=(0, 0), scale=2, angle=45)

The following code resembles window.blit(sprite, (0, 0)):

light_engine.render_transformed(sprite, pl2d.BACKGROUND, (0, 0))

2. Blitting on a Surface

If you are doing this during initialization, your method of using pygame and surface_to_texture should be fast enough, so I wouldn't bother with finding alternatives.

If you need to do this dynamically, you may want to use the shader package to avoid dropping frames. Even then, you would need to call surface_to_texture multiple times a frame to notice any lag.

You can access the graphics engine with:

lighting_engine.graphics

A texture cannot directly be rendered to another texture. Instead, you need to create a pygame_render.Layer and render onto it:

character_holding = lighting_engine.load_texture('...')
torch = lighting_engine.load_texture('...')
character_holding_layer = lights_engine.graphics.make_layer(size=character_holding.size, data=character_holding.read())
lights_engine.graphics.render(torch, character_holding_layer, <other args, you can even use shaders here>)
character_holding_torch = character_holding_layer.texture

Again, I would only do this if you need to render a texture to another texture every frame, in which case you can reuse the layer object.

Allow for resizable windows

You can now pass many arguments to specify the behavior of the window:

class LightingEngine(
    screen_res: tuple[int, int],
    native_res: tuple[int, int],
    lightmap_res: tuple[int, int],
    fullscreen: int | bool = 0,
    resizable: int | bool = 0,
    noframe: int | bool = 0,
    scaled: int | bool = 0,
    depth: int = 0,
    display: int = 0,
    vsync: int = 0
)
RomainPastureau commented 2 months ago

Incredible work! Cannot wait to test it when I have the time!