andybalaam / rabbit-escape

Android and PC game inspired by Lemmings and Pingus
http://artificialworlds.net/rabbit-escape
GNU General Public License v2.0
75 stars 41 forks source link

RFC: Hydrodynamics #298

Closed tttppp closed 8 years ago

tttppp commented 8 years ago

This isn't Minecraft, but maybe it could get a little closer...

This is a discussion of water, how it affects rabbits and how it interacts with its surroundings.

I propose four water related states:

  1. Springs - water is generated by these.
  2. Water fall - This is a square through which water is falling.
  3. River - This is water about half a square deep, which is non-fatal to rabbits.
  4. Pool - This is water a whole square deep and is fatal to normal rabbits (introduction of snorkels may change things).

Water will follow a path according to the following rules:

  1. A space with a water fall, spring or river above it becomes a water fall.
  2. A water fall with ground or pool under it becomes a river.
  3. A space with ground or pool under it and river next to it becomes a river.
  4. A horizontal row of rivers squares bounded at both ends by walls/ramps becomes a horizontal row of pool squares.
  5. A pool or river without ground under it becomes a water fall (e.g. could be caused by bashing).
  6. A pool with water fall next to it becomes a river.
  7. A horizontal collection of river tiles without a water fall or spring above them dries up (I think this rule in particular needs some thinking about).

Only solid blocks and ramps affect water - bridges have no impact.

Water movement/changes could either be at some speed, or instantaneous. I'm not sure how I feel about this.

BenjaminBalaam commented 8 years ago

I think it should be maximum 1 'N'

andybalaam commented 8 years ago

I am starting to think water dynamics could be a whole game in itself. That's not to say we shouldn't do it in Rabbit Escape - I am just wondering whether there will be a spin-off mini game where you have to get the water to the drain or something...

tttppp commented 8 years ago

I think I've got something I'm almost happy with. There's a bug in my code that means occasionally the amount of water in a cell is negative (which you can see by watching the numbers on the right), but other than that, water seems to flow ok.

basin

bulkhead

ubend

pit

Code here: https://ideone.com/iPJJL1

tttppp commented 8 years ago

I forgot to mention that this is cellular automation, whereas the previous version had a routing algorithm in it. Consequently this latest version should be much less memory intensive.

colonelfazackerley commented 8 years ago

Very exciting.

The water needs a height difference to spread, but the height difference seems a bit much. Looks a bit like sand piling piling up.

Might it help if the water had a stepped more often than the rabbits? Perhaps a bad idea: introduce a lot of complexity, like the rabbits falling 2 squares per step.

Need to see it in the game with other things moving to see if it looks OK.

Do you want me to do the animations? I can use similar techniques to the fire.

andybalaam commented 8 years ago

Wow! It does look a bit like sand but I think that is likely to provide more puzzle opportunities so looks great to me. Why do the values Flash e.g. Between N and |?

andybalaam commented 8 years ago

In the last animation the water in the hole seems to flow back out again sometimes. Is that a bug or a feature of the algorithm?

tttppp commented 8 years ago

Water flowing out from hole: I think this is a bug. Probably the same as the bug that causes negative amounts of water sometimes. If we can squash it that would be good.

Sand-like movement: I think I ended up arbitrarily reducing the spread sideways to get water flowing upwards from the U bend. I definitely had trouble building enough pressure to force water upwards.building

Animations: I would be very happy for @colonelfazackerley to do the animations. The fire looks amazing! Currently each cell has an amount of water, and I pick a character to display by using the water in a cell and the water in the cell below. The amounts are "out of" 100 (although pressure causes the numbers to go above this) (and bug causes them to go below zero).

tttppp commented 8 years ago

Oh, and ramps have a maximum capacity of 50, rather than 100.

colonelfazackerley commented 8 years ago

For choosing the animation state, I think it will need to know if the cell has water pouring down. Also possibly left or right in the pools.

tttppp commented 8 years ago

During each iteration there is a calculation of how much water to move in each of the four directions. That could be stored against the cell for animation purposes.

In a pool then water will be constantly moving between all adjacent cells. The water level stays roughly the same because it reaches an equilibrium.

colonelfazackerley commented 8 years ago

OK, that should work. Let me know when there is an interface to your code that I can work with.

andybalaam commented 8 years ago

This is very cool. I would start simple with the animations.

andybalaam commented 8 years ago

This is very cool. I would start simple with the animations.

tttppp commented 8 years ago

A few more changes, and I'm now happy. Water flows much more consistently and can still flow a reasonably long way around U-bends (although there's probably now a limit). Unless anyone has any further tests or tweaks then I suggest the next step is Java code!

Well (no more bubbling in the well): well

U-Bend (water eventually works it's way up the other side): ubend

Bulkhead (this feels very slightly less like sand now): bulkhead

Ramps (I've shown the level and the animated flow separately as it's quite hard to work out what's going on in ASCII otherwise): frame_0000 ramps

Code: https://ideone.com/upD2zy

colonelfazackerley commented 8 years ago

Nice.

andybalaam commented 8 years ago

That looks ready to me. Fantastic work.

tttppp commented 8 years ago

I've made a start on the Java implementation here: https://github.com/tttppp/rabbit-escape/tree/Water

I'm hopeful that the WaterRegion 'interface' is stable, and so animation can be done against it. There's also a new Pipe object that should have an animation.

colonelfazackerley commented 8 years ago

you used LookupTable2D :)

colonelfazackerley commented 8 years ago

A pipe should look like a tap?

tttppp commented 8 years ago

I'd thought of it as looking like a circle with water pouring out. Maybe something like this: http://thumbs.dreamstime.com/z/sewer-pipe-beach-concrete-outlet-nature-32399041.jpg

colonelfazackerley commented 8 years ago

OK. I think it should probably have a hand drawn look, as it's a bit like an entrance, in that it brings something into the world.

I think a tap might be easier, as the animation could start on the square below. The actual tap could be static.

colonelfazackerley commented 8 years ago

Note the convention for test methods. 'generateWaterTable' should be something like 'Generated_water_table_fills_correctly'.

colonelfazackerley commented 8 years ago

We need to take the state of WaterRegion

private final Set<Direction> connections;
public int capacity;
public int contents;
private Map<Direction, Integer> flow = new HashMap<>();

and note a block in the cell and make an animation for it.

I am thinking it may make sense to not use the rea files, but rather move sprites an amount related to the flow. should end up with less memory used.

colonelfazackerley commented 8 years ago

Will pipes be metal? Indestructible by bashers etc?

tttppp commented 8 years ago

I was definitely thinking pipes were indestructible. I think they're in the same category as Exits and Entrances.

I'm planning that the water will enter the cell that the pipe is in. This makes connectivity calculations simpler, as pipes don't need to worry about them.

I'm confused as to the difference between calcNewState and step. Which should the water move during? Which should the bunnies drown during? My guess is that calcNewState should set the flow, and step should move water in line with the flow, but I wanted to check this before I dive in!

colonelfazackerley commented 8 years ago

Bunnies should drown in step. You create a new Behaviour called Drowning. It has a behave method (the Rabbit class calls behave in it's step method). In Drowning.behave you call world.changes.killRabbit( rabbit ). See the Falling class.

colonelfazackerley commented 8 years ago

I'm planning that the water will enter the cell that the pipe is in. This makes connectivity calculations simpler, as pipes don't need to worry about them.

OK. I will work with that.

colonelfazackerley commented 8 years ago

I'm confused as to the difference between calcNewState and step. Which should the water move during? Which should the bunnies drown during? My guess is that calcNewState should set the flow, and step should move water in line with the flow, but I wanted to check this before I dive in!

step is called first. This does stuff, so this is when the water or rabbits move. your content values change or rabbit coordinates change. then calcNewState is called to work out what the state will be for the next step. as step is first World.init is called to set things up for the the first step.

andybalaam commented 8 years ago

As a side note I'm not happy with the activated step and calcNewState setup but I haven't thought through how to improve it yet.

colonelfazackerley commented 8 years ago

@tttppp please could you create a WaterRegionChangeListener in engine like this? https://github.com/colonelfazackerley/rabbit-escape/commit/1d649bcceb99df466d1217ed644dc4836f18776c

and create a method WaterTable.setWaterRegionChangeListener( WaterRegionChangeListener l)

Then each time a region is added because water spills in, or each time a region is deleted from the table because the water has flowed away, WaterTable uses the methods to update the listener.

colonelfazackerley commented 8 years ago

Actually, there is a way that fits in better with the current code. Please can you list the WaterRegions that are new and removed in WorldChanges.

colonelfazackerley commented 8 years ago

A suggestion for what to do with git. I pull from andybalaam/master and tttppp/Water regularly. When It's done, I will need to rebase, which is usually a problem. when it's done tttppp/Water is merged into andybalaam/master. I pull that and make a fresh branch from it. I then cherry pick my rebased commits from my old Water branch into my new one.

So, no-one else needs to rebase, but I may occassionally ask for some tweaks by @tttppp, to reduce the final merge conflicts.

tttppp commented 8 years ago

This sounds fine. I've been deliberately holding off any rebasing or history tweaking so that I don't create extra work for you @colonelfazackerley. If there are larger changes which you want in my branch then please feel free to pull request to it.

I think that at the end we will want to stabilise our work on my branch before we merge anything to andybalaam/master, because otherwise master will be broken ("Invisible water drowns rabbits. Surprise!")

I will have a look at WorldChanges. I haven't addressed bashing/digging/etc yet, but I will need to react to changes in the block table to make updates to the water table.

tttppp commented 8 years ago

We could merge tttppp/Water to master earlier if we have some very simple graphics (I'm thinking static blue rectangles), and then we could replace the graphics when we merge cf/Water. Anyway, probably something to think about for the future.

tttppp commented 8 years ago

I think changes in the blocks should now result in changes to the water table. @colonelfazackerley I've updated the WorldChanges class in a way that works, but by listing the coordinates of the changed water regions.

Water currently disappears from these regions, rather than being transferred, but it's a start.

Side note: I did this coding to Peter Rabbit (the CBBC cartoon).

colonelfazackerley commented 8 years ago

I was thinking WorldChanges would have

public  final List<>  waterRegionsEmptied = new ArrayList<WaterRegion>();
public  final List<>  waterRegionsStartedFilling = new ArrayList<WaterRegion>();

So your list of points have both new and going water regions in? I will need to have a think about wether that works for the WaterRegionRenderer.

andybalaam commented 8 years ago

Merging early is good by me. We have lots of tests to check nothing is broken. The later the merge the harder (and therefore the less effective) the review is.

tttppp commented 8 years ago

I added some very simple graphics this morning. Falling water looks horrible, but pipes and water are now visible.are To see some water falling then add a P to a space in a level.

Water doesn't yet cause drowning or extinguishing.

colonelfazackerley commented 8 years ago

I am now watching CBeebies...

I believe a full cell has 100 water units in. Could this be changed to 1024? For the animations, the cells are a nominal* 32 pixels. 32*32=1024.

This would simplify some thiings for me. Also everything should be a round number (in hex :)

tttppp commented 8 years ago

We can certainly have 1024 as the standard capacity of a cell. Cells can contain more water than their capacity, although then they are under pressure, and water will leave if possible. Does that cause any problems?

I might experiment with bridges affecting water in the future, which would result in two water regions in the same cell. Maybe water regions should have a shape to make animation easier?

colonelfazackerley commented 8 years ago

I have not thought about the pressure issue. How high does the content go?

yes regions probably should have a shape. I am mostly ignoring that for the moment.

tttppp commented 8 years ago

With the latest updates, the water model now seems to match the prototype.

@colonelfazackerley There's currently no maximum pressure (although contents is stored in an int, so I guess the answer is currently 32767). I'd be happy saying 8 x capacity was the maximum? (Or 16 x capacity if you want it to be square?) Fixing a maximum would be a good idea anyway. If a cell contains more than the maximum then is it ok for the excess to just be destroyed?

colonelfazackerley commented 8 years ago

The graphics can ignore the excess, I don't mind if you destroy it or not.

I just mean what sort of values are typically reached? Presumably it's deterministic. If you have a water column 5 world tiles deep what's the pressure (excess contents) in the bottom tile?

tttppp commented 8 years ago

@colonelfazackerley I did some experiments with a fairly deep U-bend and saw maximum values of about 500 (with the maximum capacity set to 100). The plan is for a full cell to be lethal to the rabbit - although this is made more complicated by ramps/bridges, as the rabbit's head is in the next cell up. I've pushed an initial drowning behaviour now.

colonelfazackerley commented 8 years ago

After some more thinking, I will ignore any excess capacity. I will try 10 sprites for a full cell first. actually seeing something rendered feels like it's getting closer.

Can you change the capacity units so 1024 is full?

tttppp commented 8 years ago

Ok, done. I've also added support for extinguishing fire now. The major thing left is unit tests, as I've been mainly testing by playing so far. Once I've added some tests then I'll create a pull request.

tttppp commented 8 years ago

I've started on tests and found there's a problem exposed by some solutions (eg level 12 easy).

colonelfazackerley commented 8 years ago

The water code broke something? Or you happened to notice that 12 easy is broken by something else?

tttppp commented 8 years ago

I'm sure it's the water code. It's an out of bounds exception when checking for drowning. I thought maybe that I've got the x and y coordinates mixed up at some point or something.