Updating tiles in all 4 directions on top-down level

Hello,

I’m trying to have a “lamp tile” put into an “on” state when the tile next to it is a “power tile”. Now, this works as expected, but if I were to place a second “power tile” next to the “lamp tile” and remove any of the placed “power tile” then the “lamp tile” would change to an “off” state even though there is still a “power tile” next to it.

There is also the problem of placing a “power tile” down before the “lamp tile” and then the “lamp tile” doesn’t realise it’s being powered so stays off.

gyfcat:

Code:
https://gist.github.com/AndreasElia/94faf2439204e90a3cab

Thanks in advance, I’ve been attempting to fix this all day but can’t seem to solve it! :frowning:

It looks like “normal” tiles like grass are also “neighbors” of a lamp tile in the sense of the List<Tile> neighbours parameter of your onUpdate method.
Then in your loop you disable your light bulb whenever you see a grass tile, independent of whether any previously iterated-over neighbor was a power tile.
Whether a lamp tile is enabled or not then just depends on the iteration order and where exactly a power tile is a neighbor of it.
In your onUpdate method you should remember whether you saw a “power tile” using a boolean variable and then set the light’s status accordingly after the loop.

So I managed to fix my second issue of the “lamp tile” not recognising it’s being powered if the “power tile” was placed down first, but I honestly don’t understand how to fix the first issue.

Updated LampTile.java method:


	public void onUpdate(List<Tile> neighbours) {
		setPowered(false);

		for (int i = 0; i < neighbours.size(); i++) {
			if (neighbours.get(i).getType() == Tile.power) {
				setPowered(true);
			}
		}
	}

Have you checked whether the neighbors list in the onUpdate method actually contains a power tile when it should?
I’d advise to use a debugger to find out. Check whether the construction of that neighbors list is correct.

I’ve been debugging as I’ve been going along all day. Here is some debug info of the neighbours array…


// placed a "lamp tile"
[game.level.tiles.LampTile[x=544,y=224,width=32,height=32]]

// placed a "power tile" to the left of "lamp tile"
[game.level.tiles.LampTile[x=544,y=224,width=32,height=32], 
	game.level.tiles.PowerTile[x=512,y=224,width=32,height=32]]

// placed a "power tile" to the right of "lamp tile"
[game.level.tiles.LampTile[x=544,y=224,width=32,height=32], 
	game.level.tiles.PowerTile[x=576,y=224,width=32,height=32]]

// removed the "power tile" to the right of "lamp tile"
[game.level.tiles.LampTile[x=544,y=224,width=32,height=32]]

The only solution my friend suggested to me was to in level, add the neighbours of the “lamp tile” to the array too, so that can be checked for power tiles on the opposite side. This solution seems long winded (to me) and daunting if I added a lot of power tiles if I’d need to add neighbours for all power tiles.

P.S. No grass tiles are checked since the “updates” variable is false, which is my bad for not giving all the tiles in the source.

Edit: managed to fix the issue by doing what I said above, although I absolutely HATE the idea of having to do this since it’s so long-winded and looks super in-efficient (but I have no idea how to make this more efficient and not sure for loops would even work).

Updated level code: https://gist.github.com/AndreasElia/d2f0efb38650ca586dde#file-level-java-L50

Another edit: For some reason diagonal “power tiles” keep the “lamp tile” powered, if if I remove all of the “level[x - 1][y - 2]” parts that get added to the neighbours array.

This seems very minecraft-like, and minecraft has this “issue” (with pistons being powered diagonally not being updated, but that’s a different story).

I think you would benefit if you had events for your tiles. That is, onBlockUpdate (when a tile changes, the directly adjacent tiles get onBlockUpdate called). In order to efficiently implement this, you could have a setBlock method for the Level class which will call onBlockUpdate for you, rather than directly changing the array.

Then for your lamp tile: you could have two of them sharing a common base class (LampTile): an on and an off state. In the LampTile onBlockUpdate event method, you would simply check adjacent to you (have a getBlock(x, y) thing too). If the lamp type is off, check for any PowerTile. If the lamp type is on, check for no PowerTiles.

Rather than implementing your onUpdate inside the Level class, you should be doing it in the Tile class as an abstract method so each block can have its own implementation.

Hi chrislo27,

Thanks for the ideas. I don’t have onUpdate inside the Level class, I simply pass through Tile neighbours from there. Although your suggestions make sense, I’m just looking for a quick work-around (if one exists) before diving into having dedicated tile updating with multi-state tiles (LampOnTile, LampOffTile), etc.

If you have any ideas on how to quickly get around this without having to basically re-engineer my onUpdate system, that would be amazing!

Thanks :slight_smile:

If I understand your gist correctly, the tick() method is a tickUpdate method (X times a second) and the onUpdate() is the neighbour update.

In which case you could ditch the neighbour sending system altogether, and basically have the tile onUpdate take in an x and y coordinate, which is the coordinate of the block. The lamp tile should be looking for its neighbours since no other tile really depends on neighbours right this second. The other lengthier option is the have a metadata system which is an invisible same-sized grid of integers used for the tiles, and you could use 0 and 1 for on/off. However this doesn’t really have much practicality, and in the long-run the dedicated tile classes would be ideal.

Hope that helps!

I sir, an am absolute idiot! In my previous thread I was using a similar system to what I could have used but got rid of it.

Instead of sending a load of tiles in a neighbours array, I now run level[x][y].onUpdate(level, x, y); so I can do the checks inside of the onUpdate method.

Thanks for all your help!

Edit: Turns out previous thread code was in fact useless, still stuck on how I should handle tile updating! :frowning:

Your tiles extend the Tile class. The Tile class could have a method (abstract or not) called onUpdate(Level, x, y). All the tiles get this method called (including blocks that do nothing, but since the method does nothing it’s okay). The LampTile tiles have an overriding method for onUpdate that does the check.

Example code:

Hi christlo27,

Thanks very much for your help, it’s getting late over here so I’m about to head to bed.

I can’t seem to wrap my head around it ATM, maybe because it’s so late and I’ve spent near enough the whole day working on this.

Do you have a PayPal so I can buy you a coffee as a thanks?

Here’s my code as of now (with the example you provided implemented(?)): https://gist.github.com/AndreasElia/38a5a826fb1f108de3ae

For now, the lamp tile(s) simply don’t work, or power on etc.

Thanks again, I’ll give it another go tomorrow!

I saw that the onUpdate else statement is still in the (if this instanceof LampTileOff), which means nothing gets executed for if it’s on.

Other tiny tidbits:
Here, don’t create new instances of the font and colour each frame. It can quickly use up memory leading to lag spikes as the garbage collector does its job.

I’ve also noticed your tiles are individual per block (as in, there’s a new instance for each cell). I thought you had a list of Tiles that had only one instance. It may be preferable to have a list of Tiles that are only instantiated once, and you can re-use the instance over and over again. This way your tiles don’t need an X and Y passed to them in the constructor, only in update/tick methods. Rendering will then also require an x and y value to be passed in. Just multiply the x and y by the tile width and height (g.fillRect(x * width, y * height, width, height)). One-instance tiles will bode much better in the long run if your worlds are large and expansive.

I’m still going at it, I don’t think I could sleep with this on my mind.

I did fix that issue, maybe I pasted wrong code. This is my updated code…
https://gist.github.com/AndreasElia/f600b688c677b2dbd3d7

As for the other info, thanks! I’ll be taking into account what you said and noting it down for future use, but for now the font was simply for debugging info on-screen and I will definitely look into re-using tiles.

Right now, the tiles work, but placing a new PowerTile next to a LampTileOn tile updated the LampTileOn and turns it to LampTileOff.

The ORs (||) in the on-to-off code should be ANDs (&&) since you’re checking for “this block isn’t a power one AND the next AND the next” etc.

Finally finished this single feature, thank you very much! :slight_smile:

(Changing the (||) to (&&) would only turn the light on when surrounded by power)

Updated (working to my knowledge) code:
https://gist.github.com/AndreasElia/493beea0a2414587b65a

Here’s a gyfcat to show it working: