Code Structure

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Code Structure

Post by rmcode »

This is a topic I'm constantly struggling with.

I'm working on a bomberman-like game and have troubles to get a nice code structure going. At the moment I'm basically coding something that works first and then look at how I can improve it afterwards. This means, that I'm constantly refactoring / restructuring code and new features get harder to implement over time.

My biggest problem atm is "coupling" (e.g. The player has to know where he can place bombs -> bombs have to know when to explode -> player has to know when a bomb is exploded -> explosion has to check grid for collision and removing soft walls -> other players have to know about the bombs and explosions of other players ...) and I'm not sure how to reduce it.

I know there's not a one perfect way of doing this, but I'd love to hear how other people approach this problem.
User avatar
Snuux
Prole
Posts: 49
Joined: Sun Dec 15, 2013 10:43 am
Location: Russia, Moskow
Contact:

Re: Code Structure

Post by Snuux »

Use middleclass for OOP. And seach games sources. Read and analyse it. In result, you find your favourite structure.
I like:
The Binding of Crate Box
Walk! Jump! Collect! - A Platformer Experiment (ver 2.1)
Basic platformer WIP - Demo
And many other...
My library for easy saving Slib! Try this! Now you can encrypt your save!
- Drop of light LD#30
- OUTRANGE LD#31
(Sorry for my english. I learn it myself, and I don't have enough experience)
User avatar
Plu
Inner party member
Posts: 722
Joined: Fri Mar 15, 2013 9:36 pm

Re: Code Structure

Post by Plu »

Try to use event-signalling where possible; it lets you cut away a lot of these coupling issues.

You probably have a grid table somewhere? If you add items to that grid (say, a bomb, or a soft wall) then you can send a signal from one tile to the other when things happen, and the grid-tiles can relay those events to their contents (if any)

A simple example (but this actually can be pretty complicated stuff)

Code: Select all


local tile = {
  content = nil,
  signal = function( self, signal )
    if self.content then
      self.content:signal( signal )
    end
  end,
  signalEachNeighbour = function( self, signal )
    -- left as an excercise for the reader
  end
  setContent = function( self, content )
    self.content = content
    content:setTile( self )
  end,

}

local bomb = {
  delay = 2,
  tile = nil,
  update = function( self, dt )
    self.delay = self.delay - dt
    if self.delay < 0 then
      self.tile:signal( "bomb_detonated" ) -- detonate our own square; this will clean ourselves up as well
      self.tile:signalEachNeighbour( "bomb_detonated" ) -- detonate adjacent squares
    end
  end,
  signal = function( self, signal ) 
    if signal == "bomb_detonated" then
      -- the bomb will destroy itself. it'll also be destroyed by other bombs detonating
    end
  end,
  setTile = function( self, tile )
    self.tile = tile
  end
}

local softwall = {
  tile = nil,
  signal = function( self, signal ) 
    if signal == "bomb_detonated" then
      -- the wall can now destroy itself as well
    end
  end,
  setTile = function( self, tile )
    self.tile = tile
  end
}
If you implement something like this, you can add bombs to tiles by doing tile:setContent( bomb ), walls by doing tile:setContent( wall ) and then bombs and walls will be able to interact with each other without having any knowledge of the other's working (or even existance; they're only aware that there exist a tile which they stand in and that there's an event called "bomb_detonated" which is their trigger to remove themselves )
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Code Structure

Post by rmcode »

Wow Plu, thanks for that answer! Looks like a great approach :)

I've only recently started looking for game programming patterns. Is that what you'd call an observer pattern?
User avatar
Plu
Inner party member
Posts: 722
Joined: Fri Mar 15, 2013 9:36 pm

Re: Code Structure

Post by Plu »

Yes, this is a simple implementation of an Observer Pattern :)

(I find that simple examples work better than the often overly complicated and abstract examples you find on Wikipedia)
User avatar
ArchAngel075
Party member
Posts: 319
Joined: Mon Jun 24, 2013 5:16 am

Re: Code Structure

Post by ArchAngel075 »

On finding a code structure that works and constantly reworking code to add improvements :

Personally, Ive had to rebuild my little CurseEngine about 5 times from nothing in order to optimize it.

Each time i restarted it from nothing I found it easier to add what worked extremely well from previous engines.
Like the first was a horrible mess, but taught me alot about handling stuff around update cycles.
The second got me going with an mod based asset system using OOP
the third had me implement a powerful event system that streamlined all my code
the fourth was me learning optimized methods of handling databases accross networks via proxing objects.

and now my fifth is very powerful and has a 60% complete game/mod...

I find rewriting large sections of code much more easier than i thought it would be since i knew exactly how to go about coding it all.
So dont be frightened by recoding sections, sometimes sitting down to redo a small sub section can streamline the entire process.

Also, as mentioned above the Event Listener setup is a godsend.

Goodluck with the game, i do miss bomberman :D
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Code Structure

Post by rmcode »

Snuux wrote:Use middleclass for OOP
Thanks for the suggestion! I have yet to test middleclass, but currently I'm using a closure based approach and am quite happy with it:

Code: Select all

local Foo = {};

function Foo.new()
    local self = {};
	-- insert stuff here
    return self;
end

return Foo;
Plu wrote:Yes, this is a simple implementation of an Observer Pattern :)

(I find that simple examples work better than the often overly complicated and abstract examples you find on Wikipedia)
Your example was great - the observer helped a ton with the code structure!

I have a follow up question though: I implemented the basic code for planting bombs and letting them explode, but now I'd love to know how you'd have handled the notification of the adjacent tiles.

My solution was basically just to store a reference to the grid in every tile and let the tile handle the notification of its neighbours. But now that the code is working, I can still improve it.

Code: Select all

I find rewriting large sections of code much more easier than i thought it would be since i knew exactly how to go about coding it all. So dont be frightened by recoding sections, sometimes sitting down to redo a small sub section can streamline the entire process.
I agree - It also shows you that you have improved and learned something, if you can actually improve old code :)
User avatar
Plu
Inner party member
Posts: 722
Joined: Fri Mar 15, 2013 9:36 pm

Re: Code Structure

Post by Plu »

I have a follow up question though: I implemented the basic code for planting bombs and letting them explode, but now I'd love to know how you'd have handled the notification of the adjacent tiles.
There are two options that I see. Since you're making bomberman, which uses mostly outward expanding blasts that travel along a line, I'm going with the simpler of the two because I think it'll do everything you want (and more) already.

There's a few steps to it. First off, when making your tilegrid, set each tile's neighbours in all four directions, so that they'll know what they're adjacent to.

Then, change your basic event system. Instead of just sending a string with the event, send a table. Make sure that each table has the required field "name" to describe the type event; that means you'll have all the other keys in the table to pass specific information along with the event, which the receiver can use to do follow-ups.

Now, the basic "bomb detonates" event will receive two new properties when it goes off: Strength and Direction. Strength will be the number that determines how far the explosion flies and Direction will be where it goes. The bomb itself should trigger with the direction "All", to signal that you want to it to burst in all 4 directions.

Then, the tile will need to handle this event. It now needs to do a few things. First, it needs to pass the event on to its content (you already do this). Then, it needs to catch the return value that the content gives, because the event might be modified in there (I'll explain why in a sec). And lastly, the tile might need to pass the event on to one or more of its neighbours. If the direction is set to "All", then the tile needs to create 4 new events, each with a specific direction and pass one to each neighbour. If the direction is set to a specific direction, then the tile just needs to pass that specific event to its neighbour in the proper direction.
Whatever it does; it needs to reduce the Strength stored in the event by 1 when it does. And if the Strength is already 0, it needs to stop passing the event on at all.

That should give you the basic effect of a bomberman explosion. Now for the cool part. Because the tile content receives the whole event, that means it can modify the effect that is going on. For example, if the tile content is a solid wall, all you have to do is set the Strength in the event you receive to 0, and it won't go any further than this tile. If the tile is another bomb, you can signal another event with the bomb's strength to the same tile, and have them go off together. If the tile is a soft wall then it's up to you whether the bomb effect should stop or not.

Or you can start doing really funky things and change the event's direction to make explosions travel around corners, or up their strength to make them blast further out.

And if you make each tile delay for a few milliseconds before passing the event(s) on to its neighbours, you'll get a nice outwards fanning blast instead of a sudden line of fire.

Now you'll only need to store a reference to each neighbour instead of the whole grid, and you'll have far better control. And it shouldn't be a problem that this code is in the tile, because it's very tile-specific and not very complicated at all.

If you need another code example, let me know. I have to leave so I didn't have time right now, but I'll be back later today :)
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Code Structure

Post by rmcode »

Plu wrote: Now you'll only need to store a reference to each neighbour instead of the whole grid, and you'll have far better control. And it shouldn't be a problem that this code is in the tile, because it's very tile-specific and not very complicated at all.
I don't know why I haven't thought of this. Sometimes you just can't see the forest from the trees I guess :awesome: Your posts were a great help Plu - thanks again!

I've revised the code again and this time it feels solid enough to move on to coding the AI :)
Attachments
Dark grey = 'Soft Walls', Green = 'Player', Pink = 'Blast Upgrade', Blue = 'Carry Upgrade'
Dark grey = 'Soft Walls', Green = 'Player', Pink = 'Blast Upgrade', Blue = 'Carry Upgrade'
bomberman_01.png (18.37 KiB) Viewed 4557 times
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Code Structure

Post by rmcode »

So far I'm making good progress thanks to the observer pattern. I'm using the explosion-signal's direction and strength to determine which sprite an explosion tile should use :)
Last edited by rmcode on Mon Aug 11, 2014 9:56 am, edited 1 time in total.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest