Discord
Login
Community
DARK THEME

Benchmark

Hi all,

I am working on a benchmark for a game engine I have been working on, trying to optimize it as much as possible to allow for a huge number of game objects to be in the game at one time without slowing it down too much.

Currently I have it running smoothish at 1000 objects in the world, but it is still not perfect, even with the objects not being processed outside of the camera view range. Considerably better than if it was just running with that number of objects though.

Firstly, what FPS reading are you getting from this benchmark?

Secondly, any advice on getting it to run any faster? I am using array.filter to filter objects that should be processed vs not and then iterating through them.

https://microstudio.io/i/SynKrown/darkmatter/

Chrome - 50-60 FPS Firefox - 10 FPS (for me, all JavaScript programs run with about half the FPS recorded on Chrome)

Most physics and graphics engines use a solid that surrounds a given object.

Let's assume you have a complex solid, you surround it with a larger geometry that is simple to detect collisions (usually a circle or rectangle).

The first test of whether two given objects collide is to check whether the two surrounding solids have a collision (AABB test) If so, an accurate test is performed, but it also requires more calculations.

It works very well for physics and graphics.

The second possible improvement is the organization of objects so that you do not have to check, for example, whether a given object collides with all others, but only with a smaller number of objects.

QuadTree - it is probably implemented in all physics engines.

You divide the space into fragments and create a list of objects for each fragment. Collisions are checked for objects only in a given fragment and those fragments adjacent to the currently checked one.

I achieved 800 objects and 60 FPS in QuadTree.

QuadTree should improve your collision performance 10 times.

Any other data I can provide that will help improve your engine, for comparison.

PIXI - Can display around 6000 Sprites before FPS drops below 60. MicroScript - 600 Sprites Godot - 60000 Sprites

2D collisions - up to 2000 Godot/Unity objects, simple objects, circles and rectangles.

If you want to stick with array.filter, check whether remembering the filter result is better than performing it over and over again.

Lines from the "darkmatter" file numbered 97, 111, 180, 215 - are identical. If you call this line in the same update/draw iteration, you are doing the same thing 4 times - you could only do it once.

When performing list filtering, you can perform the AABB test with the surrounding block.

I catch what you are saying about using a solid for the collisions, but I went with polygons as I couldnt find a way of calculating angles with AABB collisions. For instance, if it was a long rectangle colliding with a long rectangle. Actually, now that I have said that, it just came to mind that the solid size would just be the longest surface of the object, and then when that collision is true, use the polygon collision between those 2 objects. Which also sounds like what you were saying about surrounding the object with a larger solid. Well theres a solution to implement. Also, for platform games, polygon collisions will mad overkill, so implementing AABB is probably a good idea regardless. I am also adding a new method "checkOverlapRange(otherObject, range)" so I can allow objects to just detect polygon collisions with objects within a certain range. I do acknowledge that I may need to allow for say a massive object vs a smaller object, if the range doesnt quite reach the center of the massive object, but WIP is WIP right?

Interesting about the differences between chrome and firefox. Maybe something to do with the amount of RAM chrome uses lol...

I have looked in to QuadTree and found some useful tutorials for implementing, and have been trying to figure out how to make a class that uses a 2D array of the Level class in a similar fassion, and having the objects when outside of the level, become added to the array of the adjacent level, and only the levels that are within the cameras view port will process. That way, a person could create a game using the basic levels if they wanted, or using the level array class to allow for more open worlds, and then a 2D Elder Scrolls type game would be possible.

I am adding Mods for Matter.js once I have the other computational stuff done(I am working with bare basics for now to get as much power as possible before adding the faster libs, as then the limitations of games created with the engine will be minimal and other people might just want basic physics for their game, or both). Also, I am very proud of this basic physics Mod I have coded, as it has taken me a couple of weeks to get to the point where it is acting exactly how I had imagined.

As far as PIXI goes, I do plan on implementing it, but I notice that PIXI uses Sprites as objects, and am not able to just draw the sprite to the canvas using PIXI without it as an object. So the solution I can think of is creating the sprite inside the object, then letting the user call the sprite texture, and setting the PIXI Sprite x, y and angle to the GameObjects same variables as a replacement for drawing. I was kind of hoping I could do something similar to screen.drawSprite, so it would be a simple "if(this.usePixi) { drawPixiSprite(); }" kind of thing. I guess I still could do that, but when I tried to use PIXI, the normal "screen.draw" functions came up with errors, which makes sense as PIXI uses webGL.

I have looked at Godot and it looks like a great tool. Almost reminicent of unity. But being able to code in pure javascript, the organiztion of microStudio, the support etc and being able to access anywhere due to it being completely online make this the far superior framework in my opinion. I actually would like to figure out how to copy microStudio from github, find a server hosting site and host my own version with Dark Matter as the game back end. But in saying that, it sounds like a lot of effort with the amount I would have to learn to be able to change the source enough to make it different enough than microStudio to be original and not just a copy/paste XD.

Do you know of a quicker method than array.filter? I asked bings Copilot AI if array.filter was faster than writing my own custom loop to filter by name, and it gave me 2 different answers, for a question that shouldnt really have 2 answers imo lol.

I will have a look at those lines of code. I am sure there is a LOT of redundant code in there as sometimes I get impatient and just start trying the first thing that comes to mind, and if it works, it stays. Probably not the best method for programming, as my intention is to keep this as tidy as possible.

I am still running in to a wee issue with the level reset. I have added AI ships that fly around avoiding and shooting the black holes(rocks), but when I reset, sometimes the AI ships get removed suddenly. And it is not consistent, so is very difficult to pinpoint.

You don't rotate the boundaries of the object. PIXI has a getBounds() method - it returns the smallest area (rectangle) containing all the vertices of a given object (object, group of objects or scene).

Same in Matter.js. This area is modified every time you perform an operation on the object (rotation, adding a new object, which may change dimensions). In your engine, in my opinion, it is enough to set this dimension once so that the object will always be within the limits after making any rotation around its axis.

Determining the boundaries for a given object is to avoid making very complicated collision calculations if it is unnecessary. With the AABB limits test, you are 99% sure that you will reject the possibility of a collision correctly.

In PIXI you don't draw objects (sprite) yourself - you just pass them to the graphics engine. Then you cyclically tell only the scene to be drawn and PIXI draws all the sprites in the scene. If you want to change the Sprite position, you change the value of sprite.position.set( x, y ) . Same with turnover.

You do not use PIXI and methods from the screen object (e.g. screen.drawSprite()) at the same time - the exception is the screen.render() method. When you choose the PIXI engine, the screen object only has a render method.

Faster than filter - organize data so that you don't have to do the same work. If QuadTree is too complicated, simply divide the space into 30x30 equal rectangles so that they cover the entire area where the game takes place. Each object goes to a list of corresponding rectangles.

a = [ 0..29 ] [ 0..29 ]

a[ floor( obj.x / 100) ] [ floor(obj.y / 100) ] .push( obj )

As long as the objects are evenly distributed over the entire surface it will be as good as QuadTree.

I will take a look at the code to check what happens during restart.


I suspect that the error is related to the way you move the object during the restart. In the Matter.js engine you should use:

Body.setPosition( obj, { x: 200, y: 150 });

If the position is changed differently, the engine recalculates the force values ​​and the object starts moving very quickly. This also applies to its rotation angle.

    draw() {
      engine.draw();
      
      engine.drawEnd();
      
      screen.drawText("object count: " + this.level0.objects.length + " | objects processed: " + this.level0.enabledCount, 0, 0, 8, "red");
      screen.drawText("FPS: " + system.fps + " | " + engine.tick, 0, 7, 8, "red");
      screen.drawText("Decision: " + this.ship.decision, 0, 14, 8, "red");
      screen.drawText( Math.round(this.ship.x) + " " +Math.round( this.ship.y ) , 0, 28, 8, "orange")
 
      let count = 0
      for( let s of this.ships){
        screen.drawText( Math.round(s.x) + " " +Math.round( s.y ) , 0, 35+count, 8, "orange")
        count += 7
      }
    }

Variables are not updated.


Edit 2

If you change the names of the objects so that they do not coincide with the names of the functions, when you change the screen size, the error with the disappearing asteroids object will no longer appear.

  asteroidsObj = new _a.Asteroids();
  this.modsLib = function() { .... }
  mods = modsLib();

https://microstudio.io/i/Loginus/serialize/

Here is an example code on how to perform serialization and deserialization.

S - remembers the state D - restores the state from the moment when the S button was pressed.

The first saving is performed right after the level object is created.

Thank you for that great example you have put together. I have found the issue with the disappearing ships. It was happening due to how I was removing objects from the array. Because the timer for the bullet removal was still going off, it was pointing to the new object at the position in the array that the bullet was originally in. I fixed it by giving game objects a variable to state if they should be destroyed, and then in the level update, destroying the object rather than just on the fly.

Also, with the variables not updating, it is because my this.ship variale was no longer pointing to the original ship object, as when the level was reset, it had a whole new object in its place. I fixed that by creating a method to retrieve an object by a unique name, and updating this.ship based on that objects unique name, therefore always having a reference to the player ship object.

Ok I just noticed your updated comment, I will have a look and fix that issue. Thanks again

Post a reply

Progress

Status

Preview
Cancel
Post
Validate your e-mail address to participate in the community