Coder Social home page Coder Social logo

killedbyapixel / littlejs Goto Github PK

View Code? Open in Web Editor NEW
2.8K 35.0 140.0 6.16 MB

LittleJS is a HTML5 game engine with many features and no dependencies. 🚂 Choo-Choo!

License: MIT License

JavaScript 99.99% Batchfile 0.01%
webgl game-engine gamedev littlejs game-development codegolf js13k physics-engine sizecoding canvas

littlejs's Introduction

Frank Force is a generative artist with a background in computer graphics and game development. He is known for writing tiny programs that are often generative in nature, releasing over a thousand in total. This study has led to other larger projects like the mind bending “Dual Axis Illusion” which won Optical Illusion of the Year 2019. He also maintains a suite of open source tools for creating art, games, and music such as the LittleJS game engine and ZzFX mini sound synth. Now focused on longform generative art, he uses a wide range of visual styles to drawing on themes of emergent complexity, simulation theory, and finding connections in modern life.

littlejs's People

Contributors

bajuba avatar clementcariou avatar jwigert avatar killedbyapixel avatar lopis avatar lukenickerson avatar matthijsgroen avatar moreiramirakulo avatar paxperscientiam avatar pebutler3 avatar someunusualgames avatar thatonebro 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  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

littlejs's Issues

Direction angle for particle emitter

  1. I've spent a bit too much time than I'd like on this, figuring out how I can set the direction where the particle emmiter emits to, trying out all different combinations of params. Only to find out, I can easily do it by setting the particleEmitter.angle after creating it.

Unless you dive into the code this is really unclear - imo it would make a lot of sense to add another clear parameter for "emitAngle" that allows users to easily control use.

  1. To add onto this, the current particles can only be of square shape -> unless if I used a tile image that was of rectangular shape. But if I want to just use colored particles that are shaped like a rectangle, it's not currently possible. Am I thinking in "the wrong way again", and there's no reason for this to be added, and I should just make a rectangular tile and use that one?

Question: Tiles render direction unmatched.

@KilledByAPixel Hi Frank, when I try the engine with code:

const mapData = [
  [0, -1, 1, -1, 1, -1, 0, -1],
  [0, 1, -1, -1, 1, -1, -1, 0],
  [-1, -1, 1, -1, 1, -1, 0, 0],
  [-1, -1, 1, -1, 1, -1, 0, 0],
  [-1, 1, -1, -1, 1, 1, -1, 0],
  [0, 1, -1, 0, -1, -1, -1, 2],
]

function gameInit() {
  defaultTileSize = vec2(64)

  initTileCollision(vec2(mapData[0].length, mapData.length))

  const tileLayer = new TileLayer(vec2(), tileCollisionSize, vec2(64))

  const pos = vec2()
  for (pos.x = tileCollisionSize.x; pos.x--;) {
    for (pos.y = tileCollisionSize.y; pos.y--;) {
      if (mapData[pos.y][pos.x] >= 0) setTileCollisionData(vec2(pos.x, pos.y), 1)
      const tileIndex = mapData[pos.y][pos.x]
      const direction = randInt(4)
      const isMirror = randInt(2) > 0 ? true : false
      const color = randColor()
      const data = new TileLayerData(tileIndex, direction, isMirror, color)
      tileLayer.setData(vec2(pos.x, pos.y), data)
    }
  }

  tileLayer.redraw()

  cameraPos = tileCollisionSize.scale(0.5)
  cameraScale = 64
}

function gameUpdate() {
  //
}

function gameUpdatePost() {
  //
}

function gameRender() {
  //
}

function gameRenderPost() {
  //
}

engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, 'tiles.png')

which with tiles.png from puzzle example

image

I hopefully want the result like this:

image

but unfortunately, I got this:

image

The problem(question) is, the render result was flipped on Y axis across the mapData. Is this the correct result?

Currently, I can get picture 1 with tuning setTileCollisionData(vec2(pos.x, pos.y), 1) to setTileCollisionData(vec2(pos.x, 5 - pos.y), 1) and tileLayer.setData(vec2(pos.x, pos.y), data) to tileLayer.setData(vec2(pos.x, 5 - pos.y), data).

Is there any native solutions? Thank you.

mainCanvasSize in gameInit() does not seem to be initialized

I'm facing an issue where mainCanvasSize is zero (vec2(0,0)) when checking it in gameInit().
For reference, I am trying to create a box outside the canvas and then slide it in.
To do this, I want to pass the right edge of the canvas as position in the constructor. To get that position, I need to convert the canvas size from screen to world coord.

function gameInit() {
  worldSize = screenToWorld(mainCanvasSize)
  obstacle = new Obstacle(cameraPos.add(vec2(worldSize.x/2,0)))
  console.log(mainCanvasSize) //> Object { x: 0, y: 0 }
}

Interestingly, mainCanvas.width and .height are different when checked in gameInit() than in render and update, seems like the init is called before the engine is actually done initializing.
Am I missing something obvious?

Happens both on Firefox Nightly and Chrome, Windows 10. Page and scripts served by python -m http.server

PS: it would be nice to have a built-in worldSize counterpart to mainCanvasSize

Issue refreshing glDrawing?

I'm not sure if this is a bug, or I should be doing something I'm not.

I have an objectSpawner object, which renders a grid of tiles where you can place based on mouse position, which works like:

  1. user clicks UI, and it creates a ObjectSpawner object, and stores a global reference to "objectSpawner", so I can update it when moving mouse etc
  2. this objectSpawner has a custom render function that does something like this:
    for (var pl=0; pl < this.objectsData.length; pl++) {
    // draw above tileLayers where an object you have has certain range/grid coverage
    // get color, etc...
    drawTile(pos, this.drawSize, this.tileIndex, this.tileSize, this.color, this.angle, this.mirror);
    }

Now this all works fine generally speaking when I place the object properly (which destroyed the object, sets reference of objectSpawner = false and then, it setsData in tileLayers which renders those after), but there's an issue in a situation where user cancels placement, and tileLayers data isn't changed/refreshed (.destroy and reference are cleaned as well), and it apparantely doesn't cause a redraw of the gl layer, and leave the "grid of tiles" still rendered.

What is the correct way to "force a refresh" of the gl layer in such a situation?
I currently can do something like this to force a refresh, but it seems hacky at best - is there a more proper way to do this?

drawRect(vec2(1), 1, new Color(1,1,1,0), 0);

Use node for build system

currently it uses a bat file to build the engine...

https://github.com/KilledByAPixel/LittleJS/blob/main/src/engineBuild.bat

It should be switched over to using node.

The build process is pretty straight forward...

  • combine engine files
  • run minifier
  • run typescript declaration maker

there are a few different variations for minified, and release builds.

the main thing is setting this up for engine builds first, next we can fix up the starter project to also use that

Tilde (~) for debug mode

Hi,

I'm using a Swedish keyboard layout, which makes it impossible for me to enter the debug mode using ~. The issue with ~ is that I need to press Alt Gr + a key which needs confirmation by me pressing the space key afterwords. This doesn't seem to work in Chrome or Firefox. To work around this, I just assigned a different key by editing the debugUpdate function. I was just wondering if you would consider assigning a different key by default to make it easier for users of non-U.S. keyboard layouts?

Documentation could be improved

I finally got some spare time and started to read through the LittleJS documentation in order to gain some deeper understanding of what does what and how it does it.

What I discovered...

  • Not all code has the same coding style. Some files (engineDebug, engineExports) use fatarrow function declarations exclusively, whereas most other files use traditional js function declarations. Meanwhile, other files have a mixture of fatarrow and traditional function declarations (engine, engineDraw, engineInput). Would it be better to choose one declaration style and stick with that?

  • The engineAudio.js file has at least one function that contains no @params in its jsdoc. It also has no namespace assigned to it, and functions in the file that are not part of classes don't seem to be present in the docs as a result.

  • Most files contain @return tags but there are also some @returns tags. Even though they are basically the same, consistency would be nice.

  • Many @default tags don't have any actual values.

  • Some js files have a bunch of HTML
    scattered throughout them and others do not. Again it might be good if they were all formatted in a consistent manner.

  • The zzfxG function in engineAudio has no @param tags.

  • engine.js has no @namespace tag in it's header comment, but other js files do. Would it be better if they had consistent formatting?

  • Some js files contain @Property tags which seem to be not required.

  • engineUtilities.js contains multiple namespaces.

What I did...

Well I made a web application that loads JavaScript files and generates HTML documentation from their embedded JsDocs.

I used that to generate a new document for LittleJS and used that as a live example of what it does.

Anyhoo, that's kind of an aside. Let me know what you think.. I'm happy to help however I can.

Help using shaders

I'm having a bit of a hard time wrapping my head around of how I could apply shaders to a specific layer or all of the canvases based on time, to i.e. be able to continually animate something (for example waves on water tiles) without having to resort to sprite animations which would require redrawing that said layer every frame.

From what I've gathered so far:

  • applying a shader to the glCanvas is super straightforward -> anything using GL rendering every frame will simply work with a shader (example of that hover object in example)
  • applying shaders to tileLayer only works at the time when they are refreshed/redrawn (i.e. after I click to place an object, it redraws that tileLayer and applies the updated shader)

So if I currently have say water tiles on a tileLayer, and I'd like to use a shader every frame on it, it would require a tileLayer.redraw() every frame, which feels like an overkill. Do you have an idea how something like this would be best to achieve in the current state of the engine?

I assume your post-processing task when done would sort of allow someone to do this quite easily?

I'm attaching an example that modulates the Blue in the shader based on time. The hover grids are drawn on gl, and everything below is are tileLayers - as you can see they are not changing color, until I place something on them (Which redraws those specific tiles):

2021-11-17.14-12-32.mp4

Typo maybe?

Is there a comma missing here:

const halfDelta = vec2((posB.x - posA.x)/2 (posB.y - posA.y)/2);

Something like:

const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2); 

"abusing renderOrder" potential performance issues?

TLDR: Is it safe to use any amount of different render orders, or is it recommended for some sort of reason to stay under a certain amount?

I'm wondering about something, and if it might cause potential performance issues -> I think not, as the way renderOrder works is just a sort, but just wanted to double check in case I'm missing some intricacy of how it all works under the hood.

Essentially, if I have objects with the same renderOrder, they will be rendered in order of their creation, which can cause situation like this: (top tree being rendered on top):
image

Now this is easily solvable, if I give every tree a different render order, based on their position (i.e. rendering goes from higher pos.y down to the lowest, and from lowest pos.x to the highest), but i.e. if I had a map of 50x50 then that's potentially 2500 different render orders. I suppose that shouldn't be an issue, as the render order is just a matter of sorting the integers?

Essentially I want to render objects on different layers (but without using tileLayers as they're often animated etc), and I could keep diff objects layered properly, and avoid these render issues like so:
Say on a 10x10 map, I have undergrowth & trees layer, I could then say that all:

Undergrowth have a renderOrder of  0 + (100 - (pos.x + levelSize.x * pos.y)) =  (0 - 99)
Trees have a base a renderOrder of 100 + (100 - (pos.x + levelSize.x * pos.y)) (100 - 199)

And this should work with basically any map size and any amount of different layers (besides obviously hardware limit of how many can be rendered at the same time)

Vector2.normalize() returns (1,0) when used on a zero-length vector (0,0)

Normalization of a zero-vector is a degenerate case, since dividing by a magnitude of zero is undefined. The expected outcome in most systems is that the operation should either raise an error, or commonly just return the zero vector itself.

vec2(0,0).normalize() is the specific syntax I've reproduced the issue with.

Programmatically Triggering Inputs in LittleJS for Adaptive Gaming

Hello,

I'm currently working on a project that involves creating adaptive gaming tools using LittleJS as the engine. The goal is to demonstrate small games, such as a Space Invaders clone or your Space Huggers, that can be controlled via facial gestures or vocal commands.

My challenge is to programmatically emulate input events (e.g., key presses/releases) while maintaining the existing gamepad and keyboard functionality. Essentially, I need a way to trigger controls as if I were physically interacting with the keyboard, but via code.

Any advice or guidance on how to accomplish this would be greatly appreciated.

Thanks!

Small error in tutorial

Working through breakout tutorial and all going well until section explaining debug.

Sentence currently reads:
Debug Display
This is a good time to try opening up the debug info by pressing ~.

Tilde should be replaced by [esc] key - Had to go diving into docs to find this out.

Tiny issue but thought it might be helpful to fix

Cheers

A bit of help with changing shaders appreciated

Hey,

So I'm finally getting around to changing shaders, and I'm able to do it if I just change it "for the whole app" on runtime, but if I try to change shaders for rendering specific objects (i.e. just water tiles), this doesn't work. Basically the browser just hangs, so I'm assuming I'm doing/reiniting something that I shouldn't be when changing shaders, but it's really not obvious to me -> this is first time I'm trying to use them so I'm probably missing something super obvious about how webGL works, any chance you could have a quick look at my below code?

From some webGL examples from what I understand when changing shaders after useProgram, buffers and vertexAttribs should be re-inited for the shared you're using, but it seems this is causing some sort of overkill when done hundreds of times per frame - do you see something obvious I should just need to remove/Change a bit to get this working? And sorry for wasting your time with what I assume are some of the webGL basics 😅

//////// engineWebGL.js changes

let shaders = { // just defining some different shaders
	base: {
		vs: // vs1 string
		fs: // fs1 string
	}
	custom: {
		vs: // vs2 string
		fs: // fs2 string
	}	
};		
		
let customGLShader;
let usingShader = 0, glVertexData;

function initShaderVertextAttribArray() { // this is essentially moved from glInit, to be able to reinit when I change a shader

    // init buffers
    glCreateBuffer(gl_ARRAY_BUFFER, glVertexData.byteLength, gl_DYNAMIC_DRAW);

    // setup the vertex data array
    let offset =0;// glBatchCount = 0;
    const initVertexAttribArray = (shaderRef, name, type, typeSize, size, normalize=0)=>
    {
        const location = glContext.getAttribLocation(shaderRef, name);
        glContext.enableVertexAttribArray(location);
        glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
        offset += size*typeSize;
    };

    let program = (usingShader === 0 ? glShader : customGLShader);
    initVertexAttribArray(program, 'p', gl_FLOAT, 4, 2);            // position
    initVertexAttribArray(program, 't', gl_FLOAT, 4, 2);            // texture coords
    initVertexAttribArray(program, 'c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
    initVertexAttribArray(program, 'a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
}

function glInit() {
	... //unchanged up to below point 

	// setup vertex and fragment shaders
	glShader = glCreateProgram(
		shaders.base.vs                      // end of shader
		,
		shaders.base.fs
		// end of shader
	);

	customGLShader =  glCreateProgram(
		shaders.custom.vs
		,
		shaders.custom.fs
	);

	glVertexData = new ArrayBuffer(gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE);
	glPositionData = new Float32Array(glVertexData);
	glColorData = new Uint32Array(glVertexData);

	glBatchCount = 0;
	initShaderVertextAttribArray();
}
function glPreRender() {
	// everything is the same, instead of using glShader directly, its using this:
	
    let program = (usingShader === 0 ? glShader : customGLShader);
}



////// in-game changes:
function changeToShader(shaderName) {
    usingShader = shaderName;
    initShaderVertextAttribArray();
}


// When rendering an object, I just try to switch shader in a certain situation:

render() {
	if (this.tileIndex >= 256 && this.tileIndex < 320) { // change onyl for certain i.e. water tiles
		changeToShader(1);
	}
	// render stuff here
	
	
	if (this.tileIndex >= 256 && this.tileIndex < 320) {
		changeToShader(0); // change back to original/default shader
	}
}

isometric?

Would be super cool to get a tutorial on how to build isometric games with this. Think of fallout 1/2 etc. Ultimately I'd love to build a light weight fallout inspired (just the battles) browser game.

Is there any document?

Thanks for your great job firstly.

But is there any document about LittleJS? I know demo is great way to learn new things, but it must be very comfortable if people can learn littlejs by tutorials like "Getting started", "make you first awesome little game", "api doc"...

"?102"

What is the ?102 for at the end of each script url?

Unable to join discord

Hi, I'm unable to join the discord. I just get the message: "Whoops... Unable to accept invite". Maybe the invite has expired?

Image Element contains cross origin data.

I downloaded the base project and tried simply opening the index.html file to test if it would magically work out of the box :P but instead I got this issue. Without having to dive myself into the engine files, any ideas as to how to provide a quick and easy solution to this?

Uncaught DOMException: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-origin data, and may not be loaded.
    at glCreateTexture (file:///D:/Node/LittleJS-main/engine/engineWebGL.js?26:151:15)
    at glInit (file:///D:/Node/LittleJS-main/engine/engineWebGL.js?26:23:21)
    at Image.tileImage.onload (file:///D:/Node/LittleJS-main/engine/engine.js?26:55:9)

Thanks in advance!

Pixel perfect on chrome

I noticed the render looks blurry on Chrome.
The simplest way to fix it would be to add the following lines in the HTML files :

<style>canvas{ image-rendering: pixelated; image-rendering: crisp-edges; }</style>
Before After
image image

But you might find a better way to fix it directly in the engine itself.

prevent popups on mobile

Need to prevent the "Create Link" and magnifier lens appearing sometimes on ios mobile in chrome and safari.

Space Huggers

ESM build

I recently made a game using LittleJS and overall had a great experience. But since I like to use ES modules, it was a little awkward using the little engine because it doesn't expose the variables/methods it uses.

I'm looking to build on the previous project, and I wanted to improve this experience so I did a proof of concept turning the engine.all.js file into a file that can be imported ESM-style. See code here: https://github.com/deathraygames/wisdom-of-the-ages/tree/main/src/little-engine-esm The way it works is that if you run little-builder.js it will just append the three files together: little-header.js, engine.all.js (latest from node modules), and little-footer.js. In the footer I'm exposing all the variables and methods, and had to add a few getter and setter methods. The resulting file is little-engine-esm-build.all.js.

Work on the footer is not complete. To expand on this I would add get/set methods for all variables, and avoid exporting them directly. The downside of all of this is that makes the code somewhat larger: 177+ versus 172 KB. But to someone who loves ESM, that trade-off is worth it. Maybe someone has ideas of ways to make this smaller and/or to make the footer generation more automatic?

Would you like to have this kind of ESM build included in the LittleJS repo? If so I can fork and do a PR.

Error in puzzles example

Hi, I was looking at the examples of little.js and i found a puzzles example that does not seem to work.

Below is a screenshot of the link i used and the error shown in console
image

New LittleJS Logo

I am working on a new logo for LittleJS. Here are a few prototypes with help from AI for what I am thinking...

1 44855
OIG 13

Which of these do you prefer?

Or reply with your own ideas, any help is appreciated, thanks!

Preventing render of objects outside gl canvas?

Looking at the code I don't see any checks for engineObject rendering if they are visible inside the glCanvas.
Just wondering if this is something that's automatically detected by the renderer and if it's not, shouldn't it be built-in the engine for potential performance benefits? (assuming a game has objects that are not always visible)

Or perhaps adding an optional param to engineObject to not render when not visible?

Drawing images from texture that are not following the "tileIndex rule"

From what I've looked at this is currently unsupported, or at least can't see a "neat way" to draw an image from the texture, that is NOT following the tileSize and tileIndex "rules".

For example, say I do want to use tiles in most cases, but simply have some objects to draw which are much bigger than the tileSize (and the image has to be bigger, or even not a square, but rectangular (use case can only be inside render functions of objects, and no need to be supported usage by tileLayers).

How could I achieve this to use gl renderer, and be able to use "custom "tileImage spriteCut" parameters", i.e.:
sx ... x of which area of the texture to take
sy ... y ^^
sw ... w ^^
sh ... h ^^

Would the simplest be to adjust the drawTile tileIndex parameter, that if it's passed as object of sx,sy,sw,sh values, just calculate uvX, uvY differently? I'm not sure if that's all that's needed, as I didn't dive yet into what all the other params of bleed etc. mean.

Any help/point in the right direction much appreciated!

texture_indexes_questions

[ NPM ] Last build 1.7.11 failing

Hey, I was trying to make some little things with littlejs engine yesterday as I will start the Global Game Jam tonight and seems that the last version 1.7.11 uploaded on NPM has building issues :

Error: Process exited with code 1
✘ [ERROR] "drawTileScreenSpace" is not declared in this file

    node_modules/littlejsengine/build/littlejs.esm.js:5130:1:
      5130 │   drawTileScreenSpace,
           ╵   ~~~~~~~~~~~~~~~~~~~

✘ [ERROR] "drawRectScreenSpace" is not declared in this file

    node_modules/littlejsengine/build/littlejs.esm.js:5131:1:
      5131 │   drawRectScreenSpace,
           ╵   ~~~~~~~~~~~~~~~~~~~

Error: Build failed with 2 errors:
node_modules/littlejsengine/build/littlejs.esm.js:5130:1: ERROR: "drawTileScreenSpace" is not declared in this file
node_modules/littlejsengine/build/littlejs.esm.js:5131:1: ERROR: "drawRectScreenSpace" is not declared in this file
    at _0x345efc._evaluate (https://httpspkgsizedev-jeqp.w-corp.staticblitz.com/blitz.6854296d.js:352:378687)
    at async ModuleJob.run (https://httpspkgsizedev-jeqp.w-corp.staticblitz.com/blitz.6854296d.js:181:2372) {
  errors: [
    {
      detail: �[90mundefined�[39m,
      id: �[32m''�[39m,
      location: �[36m[Object]�[39m,
      notes: [],
      pluginName: �[32m''�[39m,
      text: �[32m'"drawTileScreenSpace" is not declared in this file'�[39m
    },
    {
      detail: �[90mundefined�[39m,
      id: �[32m''�[39m,
      location: �[36m[Object]�[39m,
      notes: [],
      pluginName: �[32m''�[39m,
      text: �[32m'"drawRectScreenSpace" is not declared in this file'�[39m
    }
  ],
  warnings: []
}

What's weird is that I don't see any 1.7.11 version or tag on github releases.

Last but not least, thank you for your work, what you do is awesome !

Use ES module or CommonJS ?

Thank you very much for providing this library, I am going to read the source code through Hello World, I know that you don't want to release NPM versions, each JS file is interactive through a lot of global variables, so it has caused a lot of doubts, which JS file from these global variables?

I think it will be easier to understand the principle of this library if we organize the code (such as import or require) in combination with the modularization of JS

Changing debug consts to let?

Wouldn't it make sense to change debug consts to let, so one can easily enable/disable debug/asserts etc. while trying out the game? It would also allow for debugging of released/live games.

I have my own debugging tools/panel for this, but still some things can be useful (i.e. FPS, objects count), so I don't have to reinvent the wheel for some of those things when I need them.

Upgrade to WebGL2

webgl2 is widely supported, we should probably change the engine over to use it. all the webgl stuff is already wrapped up super tight so it shouldn't be too much work.

Debug mode information/legend

Would appreciate an additional legend to be added to debug overlay explaining the various colors/squares:
image

I assume one of the blues is showing world pos, 1 is showing render pos of the tile or something like that? I could decipher it but a quick glimpse at what these things mean would be great to haves!

Redrawing a single tileData after changing its data

While this is a more complex use case, I could imagine it be useful to a lot, to be able to do animations on tiles when needed, and use tilelayer rendering for them whenever possible for best performance.

TLDR: I'm wondering if it's possible to change data of a tileLayer and redraw just that tile, instead of the whole layer (clearing works, but not redrawing, because of the assert check in drawTileData)

While I could just remove the assert that feels wrong, but so does redrawing the whole layer. Is there something you'd suggest doing here? If this could be done without breaking "the laws of littlejs", it would be an amazing performance improvement to run a ton of temporary animations on tiles when needed

Below is my code, which I intend to later use for basically doing animations on tiles (where I'll temporarily unset tile data, and render animation on the mainContext instead, then when animation ends, fallback to using tileData), but currently I'm just trying to get this working:

let ongoingTileAnimations = []; // stores info about any ongoing tileAnimations, which are then checked and handled every frame

function beginTileAnimation(data) {
    /* function that is used for animating stuff, i.e. if a tile has to be animated, we remove that tile from the tilelayer data,
        perform the animation in a new object we create on the mainCanvas,
        and when animation ends, we restore the original (or modified) tilelayer data at that position
     */

    /* ** signifies required params
    * Data:
    * pos/posTileIndex**
    * tileLayer**
    * animationData: duration**,
    * + any other data I might need for diff animations
    * */

    data.startedAt = realTime;
    data.endAt = realTime + data.animationData.duration;
    if (data.tileLayer) {

        data.originalTileData = tileLayers[data.tileLayer].getData(data.pos);

        tileLayers[data.tileLayer].setData(data.pos, new TileLayerData, 1); // set and clear tile
    }
    ongoingTileAnimations.push(data);
}

function processTileAnimations() { // this function is ran from gameUpdate(), and checks for any ongoing animations
    let animData;
    for(let i=ongoingTileAnimations.length-1; i >= 0; i--) {
        animData = ongoingTileAnimations[i];

        if (animData.endAt <= realTime) {
            /* for now, this examples just attempts to restore tileData after passed time, but here, the setData ASSERT error happens,
            * because the tile is attempting to render a tile, not just a color (in function drawTileData)
            */
            //console.log("animation ended here!, data: " + JSON.stringify(animData));

            tileLayers[animData.tileLayer].setData(animData.pos, animData.originalTileData, 1); // doesn't work <- assert error

            /* // this DOES work, but seems like overkill redrawing whole tileLayer
            tileLayers[animData.tileLayer].setData(animData.pos, animData.originalTileData);
            tileLayers.terrain_pri.redraw();
            */

            ongoingTileAnimations.splice(i, 1); // remove animationData
        }
    }
}

bug: touchend event problem.

The touchend event seems to be cancelable on mobile phones.

So the following code will prevent the page focus back sometime.

// try to enable touch mouse
if (isTouchDevice)
{
    // handle all touch events the same way
    let wasTouching, hadTouchInput;
    ontouchstart = ontouchmove = ontouchend = (e)=>
    {
        e.button = 0; // all touches are left click

        // check if touching and pass to mouse events
        const touching = e.touches.length;
        if (touching)
        {
            hadTouchInput || zzfx(0, hadTouchInput=1) ; // fix mobile audio, force it to play a sound the first time

            // set event pos and pass it along
            e.x = e.touches[0].clientX;
            e.y = e.touches[0].clientY;
            wasTouching ? onmousemove(e) : onmousedown(e);
        }
        else if (wasTouching)
            onmouseup(e);

        // set was touching
        wasTouching = touching;

        // prevent normal mouse events from being called
        return !e.cancelable; // 💥 touchend return false
    }
}

Puzzle Game example doesn't check the tile boundary

As result it is possible to swap a boundary tile with a wrong or even non-existent tile. For example, the following demonstrates that it is possible to swap the blue tile at (3, 0) with an imaginary tile at (3, -1), which is uninitialized and therefore interpreted as the zeroth color (red). The blue tile will then stuck at (3, -1).

Before
After

Awful performance in FireFox

*This is not really a massive issue, but more of a performance note/report.

I know you're probably aware that FF is not the most performant, but just wanted to point this out here, as it seems I can't get above 20-30 FPS in my game, and as the world scales it quickly drops, while Chrome maintains a steady 60 FPS at big worlds.

And to confirm it's not just my game causing issues, I have much worse performance allover the even simplest examples, and can't reach 60FPS in FF.

Is there anything to try and do at this stage to improve FF performance, or is it just not that great with Web GL?

Some Sprite stress tests below:

Test1: (a decent mid range computer)
FF max is FPS with just 400 sprites
FF constantly adding sprites reaches 30 FPS after 24k sprites
Chrome: 60 FPS up to 55k sprites
Chromes: reaches 30 FPS after adding 90k Sprites

Computer specs:
OS: Win 10 x64
CPU: AMD Ryzen 5 2600
RAM: 16 gig DDR4
Graphics: GTX 1050 Ti

Test2: Mobile Phone (Huawei Lite 20):
FF 60 FPS up to 2k sprites
FF constantly adding sprites reaches 30 FPS after 13k sprites
Chrome: 60 FPS up to 4k sprites
Chromes: reaches 30 FPS after adding 15k Sprites

Better multi texture support

Several users have mentioned wanting to use multiple tile sets. It would be good to think of a better way to handle rendering and loading of multiple files.

VSCode intellisense

Hello,
I noticed VSCode intellisense doesn't always work.
image
Currently, the JSDoc employs more explicit argument names than the ones utilized in the function :
image

The following solutions can be considered :

  • Use the explicit name in the code
    image
  • Write the explicit name in the argument description
    image

Improve tile indexing

Currently users set what tile in a texture by passing in a size and tileIndex to the drawing functions.

This is not ideal because all tiles must be on a grid, and really there should be a texture tile class that handles this. This allows better organization for more complex games so the tile information can be setup in a more central location or created dynamically.

The plan is to create called a new class TileTexture, that holds the uv and size of a tile. The constructor will take a size and tile index so by default it is easy to retrofit with the current system. There will also be functions to set exact pixels for the tile, to support cases where the art is not on a grid. There will also be a function to offset the tile on a grid for animation systems.

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.