I'm developing a 2D tile game engine in LibGDX and I'm asking myself how to structure the whole thing. One of the key questions right now is how to implement Player-Tile interaction similar to this particular example:
https://www.youtube.com/watch?v=Yyv5Tt5iUFc (only first seconds relevant)
We see a Player instance, a switch on a tile and something that reacts to the switch being pressed (the Door).
Basically my question is: What is this switch in "solution space"/Java OOP Terms? How to implement it? How to let it interact with the player?
I thought a lot and used Google for several hours but still didn't find any sufficient answer. Maybe I was searching with wrong words or fiddling through bad sources but I found a working solution to the problem myself which I am not very proud of since it seems stupid to me:
The map is made up of tiles where each cell has it's own properties (you can do that easily in Tiled). One of these properties is called "material". Each cell has a position in tile units and since one cell is 16x16 pixels it's position in pixels is tile units*16.
If the cell's property material value is eg. "SWITCH" then there has to exist another property which other cell this switch will manipulate if stepped on. If the cell's property material value is "DOOR" there exists another property where this door will lead to. These tiles are called EventTiles.
An EventTile is an Object that holds attributes like position, alive (for animation purposes), steppedOn, animation sprites etc. and methods like update() which basically does something with this EventTile like checking whether it's alive should be set to false and a method called init() which initializes particular attributes of a EvenTile instance. Given this information a maploader creates a HashMap of Pools of Eventtiles (since as you can see in the video switches do have different textures, animation durations than doors etc.). The keys in the HashMap are the different materials. The value for a material key is the Pool of material objects or more precise the Pool of EventTile instances. They can be created or deleted (aka alive set to false) on the fly by my Pool object. In the maploading process particular EventTile instances for e.g. SWITCH will be put in the pool with it's according positions measured in pixel units. So right now for a given map we have a given amount of SWITCH EventTiles in the pool, each living on it's own in the pool.
Why am I using a pool? Because there are other materials like SAND which basically aren't events itself but have to use animations since when the player steps on sand and moves away player has to leave footprints on the sand and by time these footprints have to fade away themselves. I thought the only way to make these work is by creating new footstep EventTiles with own animations etc. and returning them to the pool once they are faded away and since I don't want GC to work all the time I'd rather have something like 12 footstep EventTiles ready for init.
The player position is measured in pixel units. Each time player.move() is called the last thing itself does is to get the property "material" of the cell the player is currently standing at and saving it to a attribute/variable player.onMaterial. So only the player knows which material it stands on. In my opinion this is a flawed principle but I will explain this later on.
Next, the level/world class, where the tiled map and the player are living instances of each particular class, is basically doing a call on the player to hand out the information about which material player stands on and handing out player position. With these informations the code in level goes through a torrent of if-statements how to handle this particular situation.
So, if player is standing on material "SWITCH" the HashMap get's the key "SWITCH" and returns the pool of switches. Then the position of each switch in the pool is compared to the player position. If they are equal the react() method of the particular switch EventTile instance is called and action performed (like opening a Door). This again is bad style in my opinion.
Because not the switch itself reacts to player input but rather level polling every update() call whether or not the player is standing on the switch. Of course, player doesn't have to know that he stands on the switch or rather "SWITCH" material and I could have let each switch instance in the pool perform regulary switch.update() and ask the level (or the player) for player position and then if player.pos == switch.pos switch would itself
perform the action but then again using the pool is useless. Take the SAND material: Currently my map has about 120 sand tiles, given the other proposed approach there would be 2 times 120 SAND EventTile instances (my player is walking like a human being, so left foot follows right foot follows left foot..., each instance is for a different foot) which ask EVERY LEVEL.UPDATE() whether or not they have been stepped on. It's like letting a JButton poll if it has been pressed all the time instead having it being add as an EventListener.
Why not using EventListeners? Since using Threads here is not an option. The SNES didn't have threads either and they could still handle the switches correctly.
I think in complexity terms this is the same problem like in Super Mario Bros whether the Goomba knows it has been jumped on or the world is just comparing Marios Rectangle to each Goomba Rectangle, hence the title.