Repository navigation
v20.11.0
Added
- Effects: colors take an alpha. Every color option and
setColortakes[r, g, b]or[r, g, b, a], the alpha being the color's opacity ([r, g, b]stays opaque),RadialGradientEffecthonours the alpha of itsColor, andDropShadowEffectgains thesetColorthe others have. Four values used to break an effect (WebGL rejected them, and on WebGPU the fourth overwrote the next setting, such as an outline's width); a color of any other length now throws aTypeErrorsaying so - Effects:
SwayEffectleans a sprite or a layer from side to side, as if in a breeze, its top swaying most and everything from its anchor row down staying put. It is aWaveEffect, which now takes ananchorfor the row its wave fades out at, and it wraps its own time so it never stutters on a phone's medium-precision floats - Rendering:
Renderable#sortOffsetlines a renderable up as if it stood that many pixels lower (or higher) when its container sorts on"y"or"x", without moving it: a torch drawn over the wall it hangs on, dust sorted with the door it rises from, or art of different heights sorted by its base - Lighting:
Light2d#softness(0 to 1) fades the edge of a light's hole in the stage's ambient darkness, from a hard rim (0, the default, as before) to a darkness that thins straight from the center (1), along the same falloff as the glow. Hard and soft lights mix in one scene, overlapping lights multiply, and it works on every renderer - Particles:
tintOverLifegives the tints a particle takes over its life, evenly spaced and blended from the first at birth to the last at the end, such as a flame turning from white hot to deep red. It sets the color only (the fade still sets the opacity), and the blend is taken in 32 steps, which keeps the Canvas renderer to 32 tinted copies of the texture - Animation:
setCurrentAnimation(name, { onFrame })calls back with each frame index as playback reaches it, from frame 0 when the animation starts, so a footstep or a hit can land on its frame. It applies to the frame animations of aSpriteor aSprite3d - Audio:
playTrack(name, { volume, sprite })plays one region of a clip as the track and loops on it. A region of a loop's exact length keeps an MP3 loop gapless in Safari, which plays the padding an MP3 encoder adds to the end of the file - Tiled: a tile object with no class of its own is built by the class registered under its tile's class, as Tiled shows it, so a class set once on a tile in the tileset applies to every object placed from it, and
TMXTileset#getTileClass()returns that class. The object's own class or registered name still comes first, and a tile classed like a built-in (Sprite,Trigger,shape...) stays a plain tile - Tiled:
TMXObject#resolvedProperty()andresolvedProperties()return an object's custom properties together with those it inherits from its tile, the object's own winning, as the methods of the same names in Tiled's scripting API do. Only the object's own properties are set on it by name, as before - Tiled: a tileset, a tile of an image collection or an image layer whose image has a transparent color (
transin TMX,transparentcolorin JSON) is drawn with that color transparent, as Tiled shows it. The color is cleared once when the map loads, on a copy of the image - Typings:
TMXObject, the settings a class registered withregisterTiledObjectClass(or a factory registered withregisterTiledObjectFactory) receives, is exported as a type, and both functions are typed with it. A class or factory typed with its own settings still compiles - Rendering:
textureFilter: "linear-mipmap"samples a texture's mipmaps wherever it is drawn smaller than its size, so a zoomed-out camera, a minimap or a scaled-down sprite no longer shimmers as it moves; it covers sprites, text and tile layers on WebGL and WebGPU. A sprite cut from an atlas or a tileset keeps its samples inside its own frame, so its neighbours no longer streak along its edges, and a texture draws as under"linear"at its size or larger - Camera:
Renderable#visibilityLayerandCamera2d#cullMaskchoose what each camera draws: a camera draws a renderable only if they share a bit, so a minimap can leave out the particles, or aLight2dcan show in one camera only. By default every renderable is on layer 1 and every camera draws every layer; a container left out is left out with everything in it, and the layer can be set on a Tiled object as anintcustom property - Application: the
syncToDisplaysetting keeps motion steady on a display that refreshes atmaxfps(60 by default) or a whole multiple of it, such as 60 or 120 Hz. Frame times are counted in whole display frames, so a frame no longer runs no update and the next one two, which made scrolling stutter; it is off by default, andtimer.syncToDisplayswitches it at any time
Changed
- Rendering:
renderer.uvOffsetand thetextureSeamFixsetting are removed. The Canvas renderer drew tiles a pixel larger to hide the seams between them, which no longer appear; code that addeduvOffsetto a size should drop it - Rendering: a container sorted on
"x"or"y"keeps its children in order as they move. WithautoSorton, the default, it checks the order at the end of every update and re-sorts when anything is out of place, so a walking character no longer needs asort()call to pass in front of and behind things. A frame where nothing moved costs one pass over the children and causes no redraw; turnautoSortoff to keep the order you set. A"z"sort is unchanged
Fixed
- Loader: a
data:URI is decoded in place instead of fetched, so it loads on a page whose Content-Security-Policyconnect-srcdoes not allowdata:(sandboxed embeds, hardened sites), where the browser refused it. The default loading screen's logo, inlined as adata:URI, showed nothing on such pages; this covers adata:URI given to any asset type - Effects: a colored drop shadow below full opacity is no longer too bright, nor are the soft edges of an outline. Both added their color at full strength whatever its opacity
- TextureAtlas: drawing part of an image, a bitmap font glyph or a repeating pattern no longer adds a region to the atlas for every new source rectangle, and on an atlas with pages of different sizes no longer reuses the UVs worked out for the same rectangle on another page. Those regions piled up for as long as the atlas lived, and turned up among the frames an animation is built from (#1281, thanks @snowyukitty)
- Tiled: objects on isometric maps are placed where Tiled draws them. Positions are measured in tile heights on both axes, which changes maps whose tiles are not 2:1, and tile objects are anchored at their bottom center, so a game that offset them to compensate should drop that offset (thanks @vtxe02023274)
- Tiled: a tile object resized or flipped in Tiled is drawn on its own box, its tile stretched to fill it, and collides at that size. One from a tileset image was drawn and collided at the size of its tile, and one from an image collection was drawn above and to the left of its box when resized, and a whole tile width to the right when flipped (thanks @vtxe02023274)
- Tiled: tile objects are placed by their tileset's object alignment, and a rotated one turns about that point, as in Tiled: the bottom left, or the bottom center on isometric maps, unless the tileset sets another. They were always anchored at the bottom left (bottom center on isometric maps), and a rotated one turned about another point, so it was drawn and collided away from where Tiled shows it
- Tiled: a map with an animated tile object loads, and the object plays its animation. One from a tileset image threw "string parameters for addAnimation are not allowed for standard spritesheet based Texture", and one from an image collection threw a TypeError
- Tiled: in a tile layer, tiles from an image collection whose images are not all the same height each sit on the bottom of their cell, as in Tiled. Every tile was placed as if it were as tall as the tallest image of the collection
- Tiled: a collision object whose shape reaches left of or above its position, like every rectangle on an isometric map, is found by collision checks,
queryAABBandraycastwherever its shape is. Parts of it were missed, so a character could walk through walls; such an object'sposis now the top left corner of its shape - Tiled: the collision shapes of objects on isometric maps are projected with the map's tile ratio, as Tiled does. Rectangles, polygons and polylines were projected as if the tiles were 2:1, squeezed or stretched across on a map with wider or narrower tiles so they no longer lined up with the walls they outline, and ellipses were not projected at all; a circle stays an
Ellipse, and any other ellipse, which comes out tilted, becomes a polygon around it (thanks @vtxe02023274) - Tiled: a tile object on an isometric map collides over its image, as on other maps. Its default body was its image's box projected onto the map grid, a slanted shape hanging off the image
- Physics: the builtin
raycastmeasures a body from the frame its renderable draws in, as collisions have since 20.7. On a body whose anchor is not its top left corner, such as aSprite's default centre, a ray hit half the body's size away from where it collides, or missed it - Camera: a zoomed camera follows its target, stays within its bounds and centres on
focusOnin world units, andlocalToWorldandworldToLocaltake its zoom and its place on the screen (screenX,screenY) into account. Zoomed out, it kept the target off centre and scrolled past its bounds, zoomed in it stopped short of them, and a pointer on a zoomed camera, or on one drawn in a corner of the screen, hit the world somewhere else than it pointed - Effects: a glow, outline, blur, drop shadow, chromatic aberration, wave, squish, pixelate or bloom on a sprite cut from an atlas or a tileset keeps to the sprite's own frame. Along the frame's edges it picked up the neighbouring frames, and drew a glow or a line from pixels that are not part of the sprite
- Effects: a glow, outline, blur, drop shadow, chromatic aberration, pixelate or bloom measures its width, offset or size in pixels of the texture it draws, as documented, with no
textureSizeto pass: a sprite's texture, the screen for a camera's effect, a mesh's texture. Each assumed a 256x256 texture (the screen for an outline or a bloom), so on any other texture it came out thinner or thicker than asked; a game that tuned its widths around that should check them, and an effect given atextureSizekeeps it - Tiled: a tile layer drawn with smooth filtering, scaled or off the pixel grid, shows no seams between its tiles on WebGL and WebGPU. Along each tile's edge, filtering blended in the next tile of the tileset, or the spacing around it
- Tiled: a pre-rendered tile layer holds every tile it draws, on hexagonal, staggered and oblique maps and with tiles larger than their cell or offset from it, and shows the tiles
setTileandclearTilechange. It was baked at an orthogonal layer's size, which cut off the last column of a hexagonal map, what an oblique map draws left of or above its origin and the parts of large tiles reaching past the edge;setTileleft it as it was, andclearTileerased a rectangle placed as on an orthogonal map, cutting into the tiles around it - Tiled: an orthogonal tile layer with an opacity below 1, or inside a faded container, is drawn faded on WebGL and WebGPU. Its colors were drawn at full strength over what lies behind it, as bright as an opaque layer or brighter, and the container's fade was ignored
- Rendering: on the Canvas renderer, a game created with
antiAlias: falsestill draws unsmoothed after the canvas is resized, with no seams between its tiles. A resize reset the canvas's drawing state, which turned smoothing back on, and also reset the blend mode, the color and the line width, join and dash - Camera: a zoomed camera draws its floating renderables, such as a HUD or a
ColorLayer, and aColorLayerfills all of its view. It skipped them all, as a minimap does, on every renderer - Application: a
maxfpsabove 120 no longer stops the game. Updates ran every60 / maxfpsframes rounded to a whole number, which came out as 0, so none ran - Effects: changing the texture filter after an effect's texture was replaced with
setTexture, or after the effect was destroyed, no longer raises a WebGL error. The deleted texture was still listed as bound, and the filter change bound it again - Camera: the follow deadzone has its edges on whole pixels, so a camera following a target on a whole pixel comes to rest on one. Its size, a sixth of the view by default, was kept as a fraction, so with
subPixelon a camera following its target right or down stopped between two pixels - Camera:
offsetcan shift the view for good, such as to look above the hero, and not only for a shake: a shake adds its jitter to the offset and gives it back when it ends, where it used to reset it to zero. Culling, the pointer (localToWorld,worldToLocal), tile layers and parallaxImageLayers all count it, so a parallax background now shakes with the world, by its ratio - Lighting: on the Canvas renderer, a light's hole in the ambient darkness is cut where the light is, under a scrolled or zoomed camera and one drawn at an offset such as a minimap. Under a scrolled camera the mask kept the darkness inside the light and cleared it around it, and the holes of a zoomed or offset camera were cut as if it were neither
- Lighting: the ambient darkness covers the whole view of a camera. Zoomed out, it covered only as much of the world as the camera is wide, and at a fractional position, such as a camera following its target with damping, it left the first column and row lit unless
subPixelwas set - Lighting: on the Canvas renderer, a lit game whose sprites have no normal map no longer warns that normal-map lighting needs a GPU backend. It warned as soon as a
Light2dexisted, though a sprite without a normal map is drawn unlit on every renderer; it now warns once, when it draws a sprite with a normal map under a light - Audio: a sprite region declared looping (
[start, duration, true]) loops when played withaudio.play(name, { sprite }). It played only once unlessloop: truewas passed as well - Text: a label whose text is empty or only spaces no longer blanks the whole frame on WebGPU. Its canvas was sized 0 pixels wide, which WebGPU refuses to upload
- Typings:
TextureAtlas#getAnimationSettings()is typed as the settings it returns, so they pass to aSpriteconstructor from TypeScript without a cast. It returnedobject, andSprite3dtyped the named animations an aseprite atlas carries as an array - Typings:
CanvasRenderTarget#destroy(renderer)is typed and documented with itsrenderer, which frees the copy of the canvas uploaded to the GPU as well. It was hidden, so only the parent'sdestroy()showed, and TypeScript refused the argument
Performance
- Effects: sprites drawn one after the other through the same
ShaderEffectshare one draw call, as plain sprites do, even from different frames of an atlas. A uniform set to a new value draws the sprites queued before it first, so each sprite still draws with the values it was given; ascreen_textureeffect still draws each sprite on its own - Rendering: drawing a sprite, a glyph or a pattern computes its UVs from the image size instead of looking them up, with no string built and nothing allocated or kept. On the platformer, text and Jungle Rabbit examples a lookup took 34 to 58 ns and now takes 6 to 10, in Chrome and WebKit: 1 to 2% of a frame's CPU time on the text example, and too little to measure on the platformer
Contributors
@snowyukitty
- TextureAtlas: compute quad UVs instead of adding a region for every drawn rectangle (#1734) (f4c5793)
New Contributors
- @vtxe02023274 made their first contribution
Full Changelog: 20.10.1...20.11.0