1

As a fun project I decided to try and create a top down 2D game engine in HTML, CSS and JavaScript. I have the basics down, generating a grid and moving that grid when using the arrow keys to simulate walking. As a test I just used a single sprite as background, when walking up for example the bottom row height is animated from 50 to 0px, simultaneously it adds a row to the top whose height is animated from 0 to 50px. Up to there it works well and is pretty responsive.

I decided on storing all tile data in a database, then using PHP to generate an XML file on page load, store that in a javascript variable and then use find() to get the data I need. Every tile has a latitude and longitude so I can search for the tile that matches both those values and pull other relevant information like if the player can walk there, which texture it needs to have, etc.

My XML: http://www.nickbetting.nl/bit-engine/test3/createxml.php?id=1


The problem is, it's incredibly slow. I decided on a 25x15 grid of 50x50px tiles. So on initial page load it has to find the starting 375 tiles, get their data and replace the images in the tiles for the ones from the data. This initial load takes maybe 10-20 seconds.

When I then use the arrow keys to walk, it will calculate which tiles need to be rendered, then uses find() to get the data for those tiles, creates a new row and appends the tile elements with the image corresponding to that tile's texture. That also takes 1-2 seconds, which is many times slower than walking without parsing the data and finding the information.

In short:

  • I pull up the XML through ajax
  • I save that XML as an XMLdocument to a variable
  • I calculate which tiles need to be loaded based on the spawn location
  • I use find() to get those tiles from the XML stored in the var
  • When walking I add a row or column of tiles in the direction you're walking and removed a row or column tiles on the opposite direction
  • When walking those new tiles will also use find() to get the data for each tile from the XML stored in the var

This is my JS: http://www.nickbetting.nl/bit-engine/test3/js/functions.js

This is the test I have up: http://www.nickbetting.nl/bit-engine/test3/

Note for this test:

  • I'm not making a zelda clone, that's just some placeholder art
  • Walking up will get the tile data from the XML
  • Walking any other direction will just load empty grass squares without consulting the XML data
  • Walking up is how it should work, the other directions are just to compare how much time it takes to load the data

So, my question. How can I make this faster? Is there a better way to store and find data like this or is there a better way to render the new tiles in the DOM? Is jQuery just too slow for things like this?

Any other tips are also appreciated. I know that there are free and ready to download 2D JS game engines but I'm doing this as a learning experience. I generally do projects like these to see how far I can get without help and to learn new things by running into new problems.

s1h4d0w
  • 762
  • 6
  • 27
  • This question has been put on hold, but the answer I received has pretty much answered any questions I had. I will be switching over to using canvas for rendering everything, I don't really know how to edit my question to prevent it being too broad. Would it getting removed prevent me from accessing the answer I received? – s1h4d0w May 24 '19 at 21:56

1 Answers1

2

It's tough to make any recommendations on this without the advice basically boiling down to "rewrite your engine". However, there are a couple of things you're doing that could be optimised;

  1. Use a spritesheet instead of individual images (this only has to load once)
  2. Use Canvas. Manipulating the DOM is slower when editing many elements at once
  3. Store only the data you need in the database. The x, y, and sprite reference of a tile, for example.
  4. Use JSON instead of xml.

Here's a useful link to MDN regarding tile engines, tilemaps and canvas.

To address your actual query about find being slow, there's a small discussion about this here.

Update:

In regards to detecting which tiles can be walked on, you have a few choices. A couple of the popular ones are;

  1. Do as you're already doing and store a flag for this in the database
  2. Use two layers in your tilemap. One layer to render the sprites, the second to indicate which tiles can/can't be walked on. A reference that discusses this. This is useful if you want to do other things with the titles, such as slow a player down (water) or hurt a player (lava) when walked on.
Community
  • 1
  • 1
Lewis
  • 3,479
  • 25
  • 40
  • Thank you, that definitely is helpful. Only reason why I'm using separate images right now is for testing purposes, I assumed just those two images wouldn't make that much of an impact. Or does the problem lie in using an img tag instead of a CSS background image? I'll look into canvas, I know it's better but it's still hocus pocus to me which is why I tried doing it this way. On storing just x,y and sprite, how will I get data like "can I walk on this tile"? And last but not least, JSON does seem like a better candidate from the post you linked. They seemed comparable to me. Thanks again! – s1h4d0w May 24 '19 at 11:32
  • 1
    @s1h4d0w The image should be cached either way, it's just dom manipulation in general. Yeah, the learning curve of canvas is higher but definitely worth it. Worded badly, I just meant only store what you need. Updated answer to cover other points in your comment. – Lewis May 24 '19 at 11:52
  • 1
    Thanks, I accepted your answer because there's probably no fix for how I currently created it. For the walking, I wanted to implement it like "if player walks up, check if `walk == true` on tile with coordinates `curLat, (curLng-1)` and then get the data from XML or JSON. But I guess getting familiar with canvas will be the first step. In the end I want multiple layers per tile anyway, with a background, maybe some grass or part of a building. I thought I'd just create a div as a tile, place images in it and set them to `position:absolute;` to make them overlap. – s1h4d0w May 24 '19 at 12:01
  • You can hide the symptoms at least. Try rendering a couple more columns on each side, and rows on top and bottom than what you really need. Render these outside the viewport and the user won’t notice the images popping in. Alternatively, load all images you need in a doc that’s off screen one by one and that will get them cached. I’ll see if I can find my old canvas project kicking around and link it at some point. – Lewis May 24 '19 at 12:04
  • I'm actually already doing that with 1 row/column, can easily make it multiple rows. I'd need to make adding the tiles and then filling them with the sprites asynchronous, I already put the `animate` function for moving the visible sprites before the script that fills the tiles with sprites but there's still a 1-2 second delay before everything starts moving. Will have to do some more research, thanks! – s1h4d0w May 24 '19 at 12:56