Giter Club home page Giter Club logo

wasabi2d's Introduction

Wasabi 2D

PyPI PyPI - Python Version PyPI - Wheel

Discord

A fast, cutting-edge 2D game engine for Python.

Current features include:

Wasabi2D is based on moderngl(supports OpenGL 4.1+), with pygame 2.0 for some supporting functions, and supporting APIs ported from Pygame Zero.

Docs and Help

Documentation is available at https://wasabi2d.readthedocs.io/

Join us on Discord for help and announcements!

Quick example

Draw a drop-shadowed circle that follows the mouse:

import wasabi2d as w2d

scene = w2d.Scene()
scene.background = 0.9, 0.9, 1.0

scene.layers[0].set_effect('dropshadow')
circle = scene.layers[0].add_circle(
    radius=30,
    pos=(400, 300),
    color='red',
)

@w2d.event
def on_mouse_move(pos):
    circle.pos = pos

w2d.run()

Output of the above program

Installation

Use pip to install Wasabi2d from PyPI:

pip install wasabi2d

Please make sure your requirements.txt pins a major version, as Wasabi2D may continue to make breaking API and graphical changes in major versions.

Screenshots

This screenshot shows off polygons, sprites, text and particle effects:

Screenshot as of Wasabi2d 1.0.0

Roller Knight was an entry in PyWeek 28, written with Wasabi2D by Daniel Pope and Larry Hastings:

Roller Knight screenshot

Spire of Chaos was another entry in PyWeek 28 written with Wasabi2D by Daniel Moisset:

Spire of Chaos screenshot

wasabi2d's People

Contributors

alekseismyshliaev avatar anthonybriggs avatar emanueljg avatar encukou avatar grubbnuts avatar lordmauve avatar m0les avatar r1chardj0n3s avatar streetartist avatar tjguk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wasabi2d's Issues

Add ability to include camera-independent elements [feature]

The camera attribute in the scene is really useful, especially for any kind of scrolling games. When working on my pyweek entry I found that it was really hard to use when I also want some kind of "HUD" that doesn't scroll (a time counter, a hit points meter, a sidebar with help....).

My way to deal with it was implemented here: https://github.com/dmoisset/pyweek28/blob/c1ee5b49bfb5f51c9b5bb539d043f5bd8c21e961/src/hudscene.py ; I defined my own HUDScene where I extend wasabi's Scene with a separate camera and layergroup for the HUD, (lines 10-11), which are rendered on the drawmethod (line 20.... the rest was copied paste from wasabi, you can ignore it). I would have loved to have this within wasabi2d (other uses like parallax scrolling could benefit from something like this)

The approach worked fine for me (and makes layers in the HUD always above any layer in the other group). I've been thinking that this could be design in a different way (for example, adding an optional camera to a layer that overrides the one in the scene, or making easier for the user to define layer groups, each one with its own camera).

I'm happy to submit a PR with my code, or with any of the approaches above, I'm not sure what design you prefer for this.

Fixed/parallax layers

Updating the camera position currently affects all layers.

HUD components will need to be independent of camera position.

Similarly, parallax backgrounds would be only fractionally sensitive to the camera position. But could possibly be fixed in one axis.

In general one could have a camera sensitivity matrix for each layer which multiplies the camera's position when drawing the layer.

Add background/motivation/comparison to docs

Just found out about wasabi2d so I havent had the chance to try it out yet. However from reading the docs it looks like its a really nice library with some interesting ideas behind!

One thing I am curious about is what your thoughts are about other python libraries filling a similar need, like pygame for example. How does wasabi2d compare to them, or the motivation to create it in the first place?

Separate index and vertex allocators

The limitation of OpenGL 4.1 imposed on Mac OS was effectively worked around by eliminating the use of indirect rendering.

The approach for doing this is suboptimal in that it turned into multiple draw calls using the indirect rendering information.

We should instead optimise for this case by eliminating the use of the indirect buffer completely. Instead we can manage the index buffer so that we can simply draw consecutive indexes in the buffer.

Attribute permanence

If I set an attribute on a passive object, I expect that if I later get that attribute, it won't have changed. For example, I'm in control of shape.pos; my program is the only person who sets it, it doesn't change independently of of its own accord. Therefore, if I set shape.pos = xyz, and I don't change it, I expect the expression "shape.pos is xyz" to evaluate to True.

wasabi2d has an unfortunate habit of rewriting attributes to its own preferred format. For example, I believe when I write shape.pos, the shape object notices and overwites the value with a numpy 2-value array. This effectively meant that I had to store shape.pos in two places.

Please change wasabi2d so that it doesn't overwrite attributes in this way. If it wants its numpy 2-value array, it can store that in another attribute, maybe with an underscore in front (pos = _pos?).

Prevent registering a new draw() function

By adding a new draw() function decorated with @event:

@event
def draw():
    ...

The built-in draw() registration (for scene.draw()) is removed. This means that nothing draws at all. It should not be possible to register a draw function in user code.

Tone generation

Running

import wasabi2d as w
from wasabi2d import tone

scene = w.Scene()
scene.background = 0.1, 0.2, 0.4
scene.center = scene.width/2, scene.height/2

pitch = tone.create('A3', 0.5)
pitch.play()

w.run()

yields

Traceback (most recent call last):
  File "main.py", line 8, in <module>
    pitch = tone.create('A3', 0.5)
  File "/usr/local/lib/python3.8/site-packages/wasabi2d/tone.py", line 144, in create
    return _create(params)
  File "/usr/local/lib/python3.8/site-packages/wasabi2d/tone.py", line 154, in _create
    cycle = sample_gen[params.waveform](params.hz)
  File "/usr/local/lib/python3.8/site-packages/wasabi2d/tone.py", line 97, in sine_array_onecycle
    xvalues = np.linspace(0, np.pi * 2, length)
  File "<__array_function__ internals>", line 5, in linspace
  File "/usr/local/lib/python3.8/site-packages/numpy/core/function_base.py", line 113, in linspace
    num = operator.index(num)
TypeError: 'float' object cannot be interpreted as an integer

Support Windows DPI awareness

Windows can be told that a process is DPI aware using this code:

try:
    ctypes.windll.shcore.SetProcessDpiAwareness(True)
except Exception:
    pass

Without this Windows will automatically enlarge the window on a high-dpi display, giving a fuzzier quality than either directly rendering at higher resolution or using our scaler system.

We should work out the best way to support this for apps that genuinely want to render at high-dpi if available.

Nine-patch primitive

A very useful primitive in games and UIs is a "nine-patch", which is a rectangle styled with a non-repeating texture.

See for example:

The vertex/UV creation could be implemented entirely in a geometry shader, but this would require a rewrite of the Transformable class to pass pos/angle/scale as attributes rather than building a matrix. The planned ability to group primitives would also have a bearing.

Therefore the simplest way is to stick with the existing CPU-side transformations and optimise later.

animate() for tuples not respecting tween

I tried to write an animation on pos with tween=bounce_end.

However I see no bounces; I saw a very quick (linear?) tween to the target position.

import wasabi2d as w2d                                                          
                                                                                  
  scene = w2d.Scene(background="#223366")                                         
                                                                                  
  cx, cy = scene.width // 2, scene.height // 2                                    
                                                                                  
  title = scene.layers[0].add_label(                                              
      "Example",                                                 
      pos=(cx, -10),                                                               
      fontsize=48,                                                                
      align='center',                                                             
  )                       

w2d.animate(title, tween='bounce_end', pos=(cx, cy))
w2d.run()

Add set_grab() function

Pygame has a method pygame.event.set_grab() which grabs exclusive use of the input.

We could wrap wrap this but also ensure Escape (or something) releases the exclusivity, and show a message on a custom layer to explain how to exit (like how full screen works in a browser).

Line shader end segment bug

The shader for stroked lines fails to draw the end section of a line. I tried to draw this

ax = scene.layers[0].add_line(                                                  
    [(5, 100), (5, 5), (100, 5)],                                               
    color="white",                                                              
    stroke_width=2                                                              
)

and saw nothing drawn.

Question: how to decorate a method with @w2d.event ?

Hi,

In my class, I added a method to manipulate my wasabi objects (class variables)

    @w2d.event
    def on_key_down(self, key):
        if key == keys.ESCAPE:
            quit()
        elif key == keys.UP:
            self.bike.pos = (self.bike.pos[0]+1, self.bike.pos[1])

but I got this error:
AttributeError: 'Event' object has no attribute 'self'

Thanks!

Use NPOT textures for large sprites

Currently the packer will fail to pack any sprite larger than 512 pixels in either dimension.

Rather than make larger power-of-two textures to accomodate these, we should just return a single non-power-of-two texture.

These will need to be deallocated when no longer referenced.

Only 1 label per layer works

I'm trying to add 2 labels into a layer, but the second one doesn't show (it seems to make the first one bolder. I.e., if I run:

from wasabi2d import Scene, run

scene = Scene()
text1 = scene.layers[0].add_label("Hello", pos=(100, 100))
text2 = scene.layers[0].add_label("World", pos=(200, 200))
run()

I expect to see both "hello" and "world", but I only see "hello". If I comment the assignment to text1, then "World" shows up.
I think some GL object/array is being cached and reused too aggressively, but I couldn't understand how wasabi manages its caches....

Additive blending as an effect

It should be possible to write something like

scene.layers[1].set_effect('additive')

in order for all objects in a layer to be drawn with additive blending.

Perform all polygon rendering with multisampling

Currently multisampling is an option on the scene. This presents some problems - for example, screen capture from a multisample framebuffer gives undefined results.

Adding FSAA was a cheat to increase rendering quality for polygons. However, this has a global cost. Instead we can simply render the polygon VAOs to a FBO that has multisampling enabled, then render this to the screen, without multisampling. This means polygons will always be pretty and nothing else pays the multisampling cost, including the screenshot bug.

Actor compatibility shim

In Pygame Zero the Actor class was a sprite combined with a bounding box. This makes it pretty easy to do simple collision detection in a game.

Additionally one could assign arbitrary attributes to an actor which is a simple convenience.

Wasabi2d treats all primitives equivalently so Actors were not copied from Pygame Zero. And the new positioning - with rotation and scaling - means that bounding boxes can change frequently. But this makes it hard to write even basic games like Flappy Bird.

It would be useful to add an Actor compatibility layer that adds an AABB to a primitive and provides a rect-like API.

Later, better collision detection using convex polygons may be the best option.

Wasabi2d crashes at startup using only the quickstart code Ubuntu 20.04

I have a file with only the quickstart code:

from wasabi2d import Scene, run

scene = Scene()

# The rest of your code goes here.

run()  # keep this at the end of the file

I set up a new venv to try to work with Wasabi2d. I have tried in both Python 3.8.2 and 3.7.7 using pip install wasabi2d to get dependencies. Both environments gave no errors on running pip.

In either venv I get the following traceback error (with different memory addresses) when attempting to run the above code:

Traceback (most recent call last):
  File "wasabi2dtest.py", line 3, in <module>
    scene = Scene()
  File "/home/anna/venvs/py37wasabi2d/lib/python3.7/site-packages/wasabi2d/scene.py", line 92, in __init__
    ctx = self.ctx = self._make_context(width, height)
  File "/home/anna/venvs/py37wasabi2d/lib/python3.7/site-packages/wasabi2d/scene.py", line 165, in _make_context
    depth=24
pygame.error: Could not create GL context: GLXBadFBConfig
Exception ignored in: <function Scene.__del__ at 0x7ff8709a2830>
Traceback (most recent call last):
  File "/home/anna/venvs/py37wasabi2d/lib/python3.7/site-packages/wasabi2d/scene.py", line 132, in __del__
    self.release()
  File "/home/anna/venvs/py37wasabi2d/lib/python3.7/site-packages/wasabi2d/scene.py", line 121, in release
    self.layers.clear()
AttributeError: 'Scene' object has no attribute 'layers'

pip freeze on 3.7.7:

bresenham==0.2.1
glcontext==2.2.0
mapbox-earcut==0.12.10
moderngl==5.6.1
multipledispatch==0.6.0
numpy==1.19.1
pygame==2.0.0.dev10
pyrr==0.10.3
six==1.15.0
sortedcontainers==2.2.2
wasabi2d==1.4.0

pip freeze on 3.8.2 (has a few more things I was working with in regards to the project I intended to use wasabi on):

appdirs==1.4.4
black==20.8b1
bresenham==0.2.1
click==7.1.2
flake8==3.8.3
glcontext==2.2.0
mapbox-earcut==0.12.10
mccabe==0.6.1
moderngl==5.6.1
multipledispatch==0.6.0
mypy-extensions==0.4.3
numpy==1.19.1
pathspec==0.8.0
pycodestyle==2.6.0
pyflakes==2.2.0
pygame==2.0.0.dev10
pyrr==0.10.3
regex==2020.7.14
six==1.15.0
sortedcontainers==2.2.2
toml==0.10.1
typed-ast==1.4.1
typing-extensions==3.7.4.3
wasabi2d==1.4.0

System info:

System:
  Host: stormtrooper-ubuntu Kernel: 5.4.0-42-generic x86_64 bits: 64 
  Desktop: Gnome 3.36.4 Distro: Ubuntu 20.04.1 LTS (Focal Fossa) 
Machine:
  Type: Desktop Mobo: ASUSTeK model: P7P55D-E v: Rev 1.xx 
  serial: <superuser/root required> BIOS: American Megatrends v: 1601 
  date: 06/26/2012 
CPU:
  Topology: Quad Core model: Intel Core i5 760 bits: 64 type: MCP 
  L2 cache: 8192 KiB 
  Speed: 1215 MHz min/max: 1200/2801 MHz Core speeds (MHz): 1: 1204 2: 1204 
  3: 1204 4: 1204 
Graphics:
  Device-1: AMD Barts PRO [Radeon HD 6850] driver: radeon v: kernel 
  Display: x11 server: X.Org 1.20.8 driver: radeon 
  resolution: 1920x1080~60Hz 
  OpenGL: renderer: AMD BARTS (DRM 2.50.0 / 5.4.0-42-generic LLVM 10.0.0) 
  v: 3.3 Mesa 20.0.8 
Audio:
  Device-1: Intel 5 Series/3400 Series High Definition Audio 
  driver: snd_hda_intel 
  Device-2: AMD Barts HDMI Audio [Radeon HD 6790/6850/6870 / 7720 OEM] 
  driver: snd_hda_intel 
  Device-3: Logitech Webcam C310 type: USB driver: snd-usb-audio,uvcvideo 
  Sound Server: ALSA v: k5.4.0-42-generic 
Network:
  Device-1: Qualcomm Atheros AR93xx Wireless Network Adapter driver: ath9k 
  IF: wlp7s0 state: up mac: d4:6e:0e:10:4b:a9 
Drives:
  Local Storage: total: 1.47 TiB used: 740.26 GiB (49.1%) 
  ID-1: /dev/sda vendor: Samsung model: SSD 860 EVO 500GB size: 465.76 GiB 
  ID-2: /dev/sdb vendor: Western Digital model: WD1003FZEX-00K3CA0 
  size: 931.51 GiB 
  ID-3: /dev/sdc vendor: PNY model: CS900 120GB SSD size: 111.79 GiB 
Partition:
  ID-1: / size: 109.53 GiB used: 30.99 GiB (28.3%) fs: ext4 dev: /dev/sdc1 
Sensors:
  System Temperatures: cpu: 35.5 C mobo: 30.0 C gpu: radeon temp: 42 C 
  Fan Speeds (RPM): cpu: 1480 psu: 0 case-1: 1548 case-2: 0 
Info:
  Processes: 272 Uptime: 3h 43m Memory: 19.55 GiB used: 3.25 GiB (16.6%) 
  Shell: bash inxi: 3.0.38 

I think the key part of the error is the GL error: pygame.error: Could not create GL context: GLXBadFBConfig
GL is working fine in other software. The quickstart code works fine in a conda environment on my OSX laptop. But it won't work on the Ubuntu desktop.

How to render pygame Surface?

pygamezero examples use pygame Surface to render and test/set pixels, eg Tron example.

How do you render a pygame Surface in Wasabi2D?
Or is there an alternative for Surface use-cases in pygamezero examples?

Show HeadlessScene inside Scene

HeadlessScene, which renders to a texture, would be perfect as a primitive to show inside a Scene.

This would be an obvious way to create multiple viewports or a HUD that doesn't follow the camera.

Texture array for sprites and text

A current limitation in text and sprite support is that the Atlas class builds multiple textures. This forces multiple draw calls in order to render sprites that got packed into different textures. It may also prevent sprites being moved between textures. In the case of text, it currently causes the layout to crash - because a text primitive expects to generate only one pass.

The Atlas class can instead pack to a TextureArray, and return not just just texture coordinates but the index of the texture that was packed to.

This means all sprites/text will always be drawn in a single draw call, which will be more efficient, and also eliminate the current limitations.

Note that at times the Atlas will need to add new layers to the texture; it isn't clear how to do this from the ModernGL documentation. In the worst case the TextureArray could be grown by reallocating and reuploading all data.

A possible issue with line rendering / geometry shading when line spans +/- space

box-issue

yellow box is in negative quadrant and renders fine
green box is in positive quadrant and renders fine
white box spans from (-50, -50) to (50, 50) and looks odd.
The following is a code snippet one can play with and demonstate the issue:

import sys
import wasabi2d as w2d
from wasabi2d.keyboard import keys

scene = w2d.Scene(background="#223366")

# centre = scene.width // 2, scene.height // 2
centre = (0, 0)
scene.camera.pos = centre

speedlabel = scene.layers[0].add_label(
    f"{centre}",
    pos=centre,
)

# Renders OK
origin = -200
extent = -5
boundary = scene.layers[0].add_line(
    [(origin, origin), (extent, origin), (extent, extent), (origin, extent), (origin, origin)],
    color="yellow",
    stroke_width=2
)

# Renders Oddly
origin = -50
extent = 50
boundary = scene.layers[0].add_line(
    [(origin, origin), (extent, origin), (extent, extent), (origin, extent), (origin, origin)],
    color="white",
    stroke_width=2
)

# Renders OK
origin = 5
extent = 200
boundary = scene.layers[0].add_line(
    [(origin, origin), (extent, origin), (extent, extent), (origin, extent), (origin, origin)],
    color="green",
    stroke_width=2
)

@w2d.event
def on_key_down(key):
    if key == keys.Q:             # Quit
        sys.exit(0)

w2d.run()

Dependency mapbox_earcut does not work on 32bit CPython on windows.

It seems like the mapbox_earcut library requires 64bit CPython on Windows, at least when using pip install. This is a bit limiting for windows users, when downloading Python from python.org the default installer is for 32bit Python, so its very easy that you end up using that, and then mapbox-earcut and therefor wasabi2d cant be installed.

Crash with minimal window size

In some cases pygame.display.set_mode() will return a size different to the one requested for reasons other than scaling. This can cause a crash:

Traceback (most recent call last):
  File "pingplot.py", line 16, in <module>
    scene = w2d.Scene(150, 100, title="Ping plot")
  File "C:\WorkFolders\mauve\My Documents\dev\pingplot\venv\lib\site-packages\wasabi2d\scene.py", line 94, in __init__
    ctx = self.ctx = self._make_context(width, height)
  File "C:\WorkFolders\mauve\My Documents\dev\pingplot\venv\lib\site-packages\wasabi2d\scene.py", line 176, in _make_context
    self.drawer = self._make_scaler(ctx, (width, height))
  File "C:\WorkFolders\mauve\My Documents\dev\pingplot\venv\lib\site-packages\wasabi2d\scene.py", line 185, in _make_scaler
    }[self._scaler]
KeyError: False

(I added some debug output. It turns out that this is because Windows has a certain minimum window width in order to fit the title bar controls.)

Traceback (most recent call last):
  File "pingplot.py", line 16, in <module>
    scene = w2d.Scene(150, 100, title="Ping plot")
  File "C:\WorkFolders\mauve\My Documents\dev\pingplot\venv\lib\site-packages\wasabi2d\scene.py", line 94, in __init__
    ctx = self.ctx = self._make_context(width, height)
  File "C:\WorkFolders\mauve\My Documents\dev\pingplot\venv\lib\site-packages\wasabi2d\scene.py", line 176, in _make_context
    self.drawer = self._make_scaler(ctx, (width, height))
  File "C:\WorkFolders\mauve\My Documents\dev\pingplot\venv\lib\site-packages\wasabi2d\scene.py", line 183, in _make_scaler
    f"No scaler set but display size {self._real_size} != "
KeyError: 'No scaler set but display size (176, 100) != requested size (150, 100)'

numpy.VisibleDeprecationWarning, align_offset should be float when it is an array

Traceback:

Traceback (most recent call last):
  File "main.py", line 8, in <module>
    layout.inv.activate()
  File "C:\Users\Emanuel\PycharmProjects\thegame\faces.py", line 88, in activate
    title = None if not self.title else self.scene.layers[self.layer + 1].add_label(**self.title.labelize())
  File "C:\Users\Emanuel\PycharmProjects\thegame\venv\lib\site-packages\wasabi2d\layers.py", line 293, in add_label
    c = Label(
  File "C:\Users\Emanuel\PycharmProjects\thegame\venv\lib\site-packages\wasabi2d\primitives\text.py", line 120, in __init__
    self.text = text  # trigger layout
  File "C:\Users\Emanuel\PycharmProjects\thegame\venv\lib\site-packages\wasabi2d\primitives\text.py", line 154, in text
    self._layout()
  File "C:\Users\Emanuel\PycharmProjects\thegame\venv\lib\site-packages\wasabi2d\primitives\text.py", line 207, in _layout
    verts[glyph_slice] = glyph_verts + (x - align_offset, yoff - descent, 0)
numpy.VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to d
o this, you must specify 'dtype=object' when creating the ndarray

3.8 environment:

mapbox-earcut==0.12.10
moderngl==5.5.4
MouseInfo==0.1.3
multipledispatch==0.6.0
numpy==1.19.0
Pillow==7.1.2
PyAutoGUI==0.9.50
pygame==2.0.0.dev6
PyGetWindow==0.0.8
PyMsgBox==1.0.8
pyperclip==1.8.0
PyRect==0.1.4
pyrr==0.10.3
PyScreeze==0.1.26
PyTweening==1.0.3
six==1.15.0
sortedcontainers==2.2.2
wasabi2d==1.3.0

Debug output after breakpoint at verts[glyph_slice] = glyph_verts + (x - align_offset, yoff - descent, 0)
unknown

Allow setting matrix directly

In order to support loading objects from SVG the ability to read/set the transformation matrix (at least the 3x2 upper part) of a Transformable could be exposed.

The matrix can be decomposed to a position, scale and rotation although Transformables cannot currently represent shear.

Texture repack

When building texture atlases, sprite data is uploaded from system memory.

Sprites that are unused after a long time are not currently deleted. It would be possible to "garbage collect" sprites - perhaps periodically or as a function that users can call. To release texture memory this could rebuild the atlas with sprites to keep.

However, at this point, the sprite data is on the GPU already. A new texture can be built by drawing the old sprites into their new locations - essentially by drawing them as sprites into a new texture.

Circle.segments not recalculated when change radius

I found this doing a quick porting of Tron demo from PyGameZero to entice my son to try making his own games.
When I animated the explosion circle it was a pentagon.

FYI:

  • Circle.segments and polygon isn't refreshed in _on_set_radius()
  • Circle segments constructor parameter isn't exposed by Layer.add_circle()
    • I'd looked to that as a workaround, if its undesirable to rebuild the opengl polygon when resizing a circle

Demo of problem:

import wasabi2d as w2d

scene = w2d.Scene(width=800, height=800)

circle_starts_small_filled = scene.layers[1].add_circle(
            pos=(200, 200),
            radius=2,
            color='CYAN',
            fill=True
        )
circle_starts_small = scene.layers[1].add_circle(
            pos=(200, 600),
            radius=2,
            color='CYAN',
            fill=False
        )
circle_starts_bigger_filled = scene.layers[1].add_circle(
            pos=(600, 200),
            radius=20,
            color='GREEN',
            fill=True
        )
circle_starts_bigger = scene.layers[1].add_circle(
            pos=(600, 600),
            radius=20,
            color='GREEN',
            fill=False
        )
w2d.animate(circle_starts_small, duration=5, radius=200)
w2d.animate(circle_starts_small_filled, duration=5, radius=200)
w2d.animate(circle_starts_bigger, duration=5, radius=200)
w2d.animate(circle_starts_bigger_filled, duration=5, radius=200)

# Found that segments is set on construction, so these get smoother:
w2d.animate( scene.layers[1].add_circle(
            pos=(200, 600),
            radius=5,
            color='CYAN',
            fill=False
        ), duration=5, radius=150)
w2d.animate( scene.layers[1].add_circle(
            pos=(200, 600),
            radius=10,
            color='CYAN',
            fill=False
        ), duration=5, radius=120)

w2d.run()

Stroke and fill in single VAO

Currently shapes may be either filled or not.

This was because the generic VAO class only maintains one buffer of indirect commands and makes only one draw call.

A filled, stroked shape can share vertices; only the draw mode and index sequence will differ. So these could be held in a single VAO.

We could subclass VAO to allow separate index allocations for stroke and fill commands. These could share the index buffer and simply exist as separate indirect draw commands.

Maybe the way to achieve this is to split out a separate manager for indirect draw commands currently integrated with the VAO code here to allow multiple per VAO.

Additionally shapes should be able to toggle filling and stroke visibility after creation, probably by zeroing and resetting the number of indices to draw within the draw command (a draw command of 0 vertices should be a no-op). The ability to set this would also allow line trails that grow without needing to be reallocated.

Text layout query

Often in order to position or scale text correctly you need to know how it will be laid out.

It should be possible to query this information without modifying a text object (perhaps even without creating one).

Multiplicative blending as a chain effect

It should be possible to write

scene.chain = chain.Multiply(
    chain.LayerRange(stop=0),
    chain.Layer(1)
)

in order to composite the scene with layer 1 multiplying all lower layers. This would allow for lighting effects.

Online pack sprites from loader

wasabi2d includes an image loader that it inherits from Pygame Zero for loading images from the images/ directory. However this is not currently used in texture packing.

Currently rectpack's offline mode is used and we pass a hard-coded list of images to pack. Even pre-packing all the images in the images directory would be better than this. However, online packing the images that actually get requested is a little more scalable. Sadly rectpack's online solver does not return the location to pack the next image - an issue that is relatively but not completely shallow - see rectpack#27.

We must no longer convert the alpha channel (does this premultiply)?:
https://github.com/lordmauve/wasabi2d/blob/master/wasabi2d/loaders.py#L183

Refactor: global dirty list

Various objects track their own dirty state. Then before drawing the engine crawls a reference tree to update all the things that need to be updated.

This is inefficient and also adds complexity. We could just maintain a global(ish) "dirty list" - probably actually a WeakSet - that objects add themselves to when they are dirty.

There needs to be some ordering involved in this: objects that are dirty in system memory need to get updated before buffers that are dirty get copied to the GPU.

Prevent assigning to immutable attributes like `.layer`

Assigning to layer succeeds but produces a layer failure:

Traceback (most recent call last):
  File "c:/Bookworm Adventures Monster Maker/MonsterMakerArcade.py", line 77, in <module>
    run()
  File "C:\Users\name\AppData\Local\Programs\Python\Python37\lib\site-packages\wasabi2d\game.py", line 175, in run
    updated |= self.dispatch_event(event)
  File "C:\Users\name\AppData\Local\Programs\Python\Python37\lib\site-packages\wasabi2d\game.py", line 158, in dispatch_event
    handler(event)
  File "C:\Users\name\AppData\Local\Programs\Python\Python37\lib\site-packages\wasabi2d\game.py", line 136, in new_handler
    return handler(**prepped)
  File "c:/Bookworm Adventures Monster Maker/MonsterMakerArcade.py", line 30, in on_mouse_move
    limb.checkIfSelected(position = pos, rel = rel)
  File "c:\Bookworm Adventures Monster Maker\Limb.py", line 13, in checkIfSelected
    self.selectionbox.pos += rel
  File "C:\Users\name\AppData\Local\Programs\Python\Python37\lib\site-packages\wasabi2d\primitives\base.py", line 61, in pos
    self._set_dirty()
  File "C:\Users\name\AppData\Local\Programs\Python\Python37\lib\site-packages\wasabi2d\primitives\base.py", line 174, in _set_dirty
    self.layer._dirty.add(self)
AttributeError: 'int' object has no attribute '_dirty'

New filter primitives

We should expand the list of filter primitives with reference to those defined for SVG.

Some of these would be immediately useful

  • Color matrices, and the shorthand versions like grayscale, sepia etc.
  • Dilate/erode
  • Vignette

Some, like compositing operations, would need a system of filter wiring so that the output of one filter can be the input to another, and potentially so that multiple layers can be the inputs.

Choose definitive vector type

Although it doesn't use it internally, Wasabi2d re-exports Pygame's pygame.math.Vector2 class.

Vector2 is easier to use than a numpy.ndarray. It has methods for things like magnitude and normalisation. One place where it fits poorly with Wasabi2d is that it uses degrees in various methods. Wasabi2D takes the view that radians are the one true coordinate system. There are radians alternatives for some methods, but these have longer names. as_polar() and from_polar() do not have a radians version.

I also feel strongly that vectors should be immutable and hashable (eg. to use as keys in a spatial hash). pygame.math.Vector2 is not.

If we have a great candidate for a vector, it should be exposed as the return value of Transformable.pos, for example; these currently return a mutable numpy.ndarray.

Coroutines

It should be possible to use coroutines in games. This could be a feature of the clock:

async def move_to(dest):
    delta = player.pos - dest
    async for dt in clock.async_frames(seconds=1):
        player.pos += delta * dt

clock.run(move_to(x, y))

or

async def show_game_over():
     msg = scene.layers[99].add_label("Game over")
     await clock.async_sleep()
     msg.delete()

clock.run(show_game_over())

OpenGL 4.3 not supported on Apple

Trying to run any example results in output like the following:

$ python3 examples/lander/lander.py
2019-09-17 18:24:57.234 Python[8697:125101] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to (null)
Traceback (most recent call last):
  File "examples/lander/lander.py", line 28, in <module>
    title="Lunar Lander",
  File "/Users/Rob/wasabi2d-venv/lib/python3.7/site-packages/wasabi2d-0.1.0-py3.7.egg/wasabi2d/scene.py", line 57, in __init__
    depth=24
pygame.error: Failed creating OpenGL context at version requested
(wasabi2d-venv)
$

Sadly it seems that the most recent supported version of OpenGL on Apples is 4.1 https://support.apple.com/en-us/HT202823

Manually hacking the version in scene.py to 4.1 just results in a segfault (probably as expected). OpenGL 4.1 examples in moderngl (say) work fine.

Move the MSAA stuff to a chain effect

The MSAA stuff for polygons is too expensive to be turned on by default.

Rather than throwing it away, it could be made into a chain effect, so that it affects all primitives in a LayerRange but is optional to turn on.

Wasabi doesn't update sprites unless they've moved

I noticed this last night while I was porting my alien joypad demo from Pygame Zero to Wasabi. If you update a sprite's image but not it's position, then Wasabi will keep displaying the old image. I'm assuming this is due to a caching optimisation somewhere. This is on the most recent Wasabi2D from pip, running on Windows 10, installed last night.

You can see this in action in the demo here. When the sprite stops moving, it isn't updated and keeps the old run or jump image. Moving the sprite, even by 0.1, seems to force a refresh.

Let me know if you need a simpler replication and I can rig one up.

Camera should be a transformable

Currently the camera can be moved by assigning to screen.camera.pos. But it should be a full Transformable - which then offers rotation, scaling, and assignment to .x and .y.

spin_drag, rotation_spread for particles

Particles can have a "drag" effect which slows their motion. However there is no corresponding parameter for slowing their spin (angular velocity).

Additionally, particles may have a spin_spread which affects the distribution of their spins. However, there is no parameter controlling their initial angle and distribution of the same.

These will be needed to create certain effects.

Add gather() for coroutines

asyncio.gather() is a function for awaiting several coroutines at once.

Users should be able to write something not dissimilar to

await gather(
    some_coro(),
    another_coro()
)

Perhaps related, the task object returned by clock.coro.run() should have an async def join() so that you can await it finishing:

task = clock.coro.run(...)

await task.join()

This can be used to implement gather:

async def gather(*coros, clock=None):
    clock = clock or get_my_clock()
    tasks = [clock.coro.run(t) for t in coros]
    for t in tasks:
        await t.join()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.