dmatter

Members
  • Content count

    2740
  • Joined

  • Last visited

  • Days Won

    2

dmatter last won the day on August 13

dmatter had the most liked content!

Community Reputation

4844 Excellent

1 Follower

About dmatter

  • Rank
    Contributor

Personal Information

Social

  • Twitter
    daveagill
  • Github
    daveagill

Recent Profile Visitors

17488 profile views
  1. What am I not understanding about programming?

    I suspect you're hitting Analysis Paralysis by getting too hung up on doing things the "OO way". Pong is really no different to any other kind of application: It gathers input It processes that input into some state It presents that state as output The only notable variation is that for a real-time game those 3 step needs to occur every single frame. So all we do is wrap them up in a game loop like this: World world; while (true) { Keys keysPressed = checkInputs(); // input update(keysPressed, world); // process render(world); // output } World maintains the state of the game (paddles, players, scores, ball, etc) and its public methods model the interactions that you have available to you when playing. In the case of Pong it might have a method like: movePaddle(amount). The update() function knows how to convert inputs (keypresses) into actions that modify the world. For example it knows that if you push the right-arrow key then it will invoke: world.movePaddle(1); The job of the render() function is to translate the state held by World into something visual. If the process of rendering requires state all of its own (textures, shaders, models, etc) then a simple render function isn't enough. That's fine. Just make it a method in a class: renderer.render(world); This class can now hold all that rendering gumpf, available only to the render() method. Of course as you scale this up to larger and larger applications/games you'll find that these pieces become bloated. At which point you'll want to start sub-dividing ("refactoring") them. For example you can move code out of your World class into Paddle and Ball classes and World just glues them together. Or you might move code out of render() (or Renderer) into renderPaddle, renderBall, etc. This "refactoring" process helps keep your code organised and maintainable. But it's not where you start. You start simple and you subdivide structure into it only when needed. This is how we all do it! With lots of experience we developers can sometimes foresee what sort of factoring is appropriate and then shoot straight for that. But in reality it's an unreliable process (because we're only human, we stumble across pitfalls or we learn something new or requirements change). Ultimately it's a skill that you will never master and you will never stop improving!
  2. Day 2

    WoA? What's going on here? Are you making this game for real or purely theoretical?
  3. Yes why not be a judge yourself next year? Re Rant1: Personally I don't believe a judge would need to play a game through to completion to accurately award the categories. Ideally they would play more than the bare minimum, sure, but attaining 100% might not even be feasible. Re Rant2: While tis unfortunate jbadams was unable to submit his scores before the cutoff don't forget that it affected all entries not just yours. Imagine if 3 judges had been the plan all along then you wouldn't even feel slighted. So maybe don't sweat the small stuff? I do agree more judge scores would likely reduce the influence of anomalous results on the overall average. Having said that, a small number of judges who bias against various traits adds a dynamic to the competition. For example Riu is obviously a graphics guy and conceivably you could exploit that fact or it could sting you hard! If we had a bio for each judge it would help with that. You guys made a good game. It has flappy-bird-esque difficulty, good graphics, it's entertaining to watch other people play, the idea is novel, it's complete and bug-free! Don't be disappointed, be proud!
  4. WoA V - Afterparty/Judging thread

    Congrats all!! There were some very exceptional games made this year that people can be pretty damn proud of! I just want to give a huge shout out to @slicer4ever for running the show, to his fellow judges (@riuthamus , @Alpha_ProgDes and @jbadams) for their valuable time and to Slicer, @jpetrie and @Lactose for their valuable cash into the prize pool. A very generous bunch of people! As always that was a lot of fun!
  5. This is a technical article about how I implemented the fluid in my game “Invasion of the Liquid Snatchers!” which was my entry for the fifth annual "Week of Awesome " game development competition here at GameDev.net. One of the biggest compliments I’ve received about the game is when people think the fluid simulation is some kind of soft-body physics or a true fluid simulation. But it isn’t! The simulation is achieved using Box2D doing regular hard-body collisions using lots of little (non-rotating) circle-shaped bodies. The illusion of soft-body particles is achieved in the rendering. The Rendering Process Each particle is drawn using a texture of a white circle that is opaque in the center but fades to fully transparent at the circumference: These are drawn to a RGBA8888 off-screen texture (using a ‘framebuffer’ in OpenGL parlance) and I ‘tint’ to the intended color of the particle (tinting is something that LibGDX can do out-of-the-box with its default shader). It is crucial to draw each ball larger than it is represented in Box2D. Physically speaking these balls will not overlap (because it’s a hard-body simulation after all!) yet in the rendering, we do need these balls to overlap and blend together. The blending is non-trivial as there are a few requirements we have to take into account: - The RGB color channels should blend together when particles of different colors overlap. -- … but we don’t want colors to saturate towards white. -- … and we don’t want them to darken when we blend with the initially black background color. - The alpha channel should accumulate additively to indicate the ‘strength’ of the liquid at each pixel. All of that can be achieved in GLES2.0 using this blending technique: glClearColor(0, 0, 0, 0); glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE); Putting all that together gets us a texture of lots of blurry colored balls: Next up, is to contribute this to the main backbuffer as a full-screen quad using a custom shader. The shader treats the alpha channel of the texture as a ‘potential field’, the higher the value the stronger the field is at that fragment. The shader compares the strength of the field to a threshold: Where this field strength is strong enough then we will snap the alpha to 1.0 to manifest some liquid. Where the field strength is too weak then we will snap the alpha to 0.0 (or we could just discard the fragment) to avoid drawing anything. For the final game I went a little further and also included a small window around that threshold to smoothly blend between 0 and 1 in the alpha channel, this softens and effectively anti-aliases the fluid boundary. Here’s the shader: varying vec2 v_texCoords; uniform sampler2D u_texture; // field values above this are 'inside' the fluid, otherwise we are 'outside'. const float threshold = 0.6; // +/- this window around the threshold for a smooth transition around the boundary. const float window = 0.1; void main() { vec4 col = texture2D(u_texture, v_texCoords); float fieldStrength = col.a; col.a = smoothstep(threshold - window, threshold + window, fieldStrength); gl_FragColor = col; } This gives us a solid edge boundary where pixels are either lit or not lit by the fluid. Here is the result after we apply the shader: Things are looking a lot more liquid-like now! The way this works is that when particles come within close proximity of each other their potential fields start to add up; once the field strength is high enough the shader will start lighting up pixels between the two particles. This gives us the ‘globbing together’ effect which really makes it look like a fluid. Since the fluid is comprised of thousands of rounded shapes it tends to leave gaps against the straight-edged tilemap. So the full-screen quad is, in fact, scaled-up to be just a little bit larger than the screen and is draw behind the main scene elements. This helps to ensure that the liquid really fills up any corners and crevices. Here is the final result: And that’s all there is for the basic technique behind it! Extra Niceties I do a few other subtle tricks which help to make the fluids feel more believable… Each particle has an age and a current speed. I weight these together into a ‘froth-factor’ value between 0 and 1 that is used to lighten the color of a particle. This means that younger or faster-moving particles are whiter than older or stationary parts of the fluid. The idea is to allow us to see particles mixing into a larger body of fluid. The stationary ‘wells’ where fluid collects are always a slightly darker shade compared to the fluid particles. This guarantees that we can see the particles ‘mixing’ when they drop into the wells. Magma particles are all different shades of dark red selected randomly at spawn time. This started out as a bug where magma and oil particles were being accidentally mixed together but it looked so cool that I decided to make it happen deliberately! When I remove a particle from the simulation it doesn’t just pop out of existence, instead, I fade it away. This gets further disguised by the ‘potential field’ shader which makes it look like the fluid drains or shrinks away more naturally. So, on the whole, the fading is not directly observable. Performance Optimisations As mentioned in my post-mortem of the game I had to dedicate some time to make the simulation CPU and Memory performant: The ‘wells’ that receive the fluid are really just colored rectangles that “fill up”. They are not simulated. It means I can remove particles from the simulation once they are captured by the wells and just increment the fill-level of the well. If particles slow down below a threshold then they are turned into non-moving static bodies. Statics are not exactly very fluid-like but they perform much better in Box2D than thousands of dynamic bodies because they don’t respond to forces. I also trigger their decay at that point too, so they don’t hang around in this state for long enough for the player to notice. All particles will eventually decay. I set a max lifetime of 20-seconds. This is also to prevent the player from just flooding the level and cheating their way through the game. To keep Java’s Garbage Collector from stalling the gameplay I had to avoid doing memory allocations per-particle where possible. Mainly this is for things like allocating temporary Vector2 objects or Color objects. So I factored these out into singular long-lived instances and just (re)set their state per-particle. Note: This article was originally published on the author's blog, and is reproduced here with the author's kind permission.
  6. ^ Exactly. If you're dispatching behaviour per-type from a pure-virtual-interface, then you're probably on steady ground with inheritance (†). But if you're trying to "common stuff out" with inheritance according to what you perceive is common between classes then you're probably doing it wrong. The design of interfaces should actually be driven from the needs of the caller: What behaviour do callers need to dispatch in a type agnostic way? † Unfortunately inheritance forces you to make an up-front decision about which interfaces you want to implement, at the author-time of the class. Then the dispatch is performed at run-time. Most mainstream languages work this way. But that can mean that inheritance is still a poor choice for certain use-cases.
  7. Ah but even that's not what I consider to be a good use of inheritance! Here you just need two free factory functions: Solider BLU_Missile_Soldier(); Soldier RED_Rifleman_Soldier(); (You also don't need a virtual destructor because you have nothing to destruct in the subclasses). (You are also potentially leaking memory in that snippet - but that's unrelated to inheritance).
  8. Yes I agree with that. But not all tools should be used equally. If inheritance is your hammer then everything will start to look like a nail. Really it should be that tool at the bottom of your toolbox that you get out begrudgingly sometimes while muttering noises about how C++ should just have better constructs to solve the problem at hand. (Even though you know that's never gonna happen for C++) (Note, just to reiterate: I'm not talking about pure virtual base-classes or interfaces or protocols or traits or whatever your favourite language calls them. I've few gripes with those. I'm talking about any other uses of inheritance). Sure! I mean call it whatever's fashionable, I just call it a list of bounding volumes E.g. in C++: std::vector<BoundingVolume> (emphasis mine). I honestly don't see the problem with that. This isn't something I would be trying to 'solve' my way out of. In my games that happens all the time! Usually I'm not interested in just testing "if there was a collision", usually I want to know what was collided with so that there can be different behaviours in response to different kinds of collision. For example I want different behaviours when my player hits a solid wall compared to if they hit water or lava or they get hit by a bullet or they get a health pickup. That's 5 different object types (Wall, Water, Lava, Bullet, Health) with 5 different reactive behaviours for my player upon collision. One way (but by no means the only way) of handling this might actually be to loop over each type of thing separately (psuedo code): func processCollisionsWith(things, interaction): foreach thing in things: if collided(thing, player): interaction(thing, player) processCollisionsWith(walls, HitWallInteraction) processCollisionsWith(water, BeginSwimmingInteraction) processCollisionsWith(lava, BurntAliveInteraction) processCollisionsWith(bullets, TakeBulletDamageInteraction) processCollisionsWith(healthpickups, HealthBonusInteraction) This also means that I get to decide the processing order, for example if I want all health-pickups to be handled before anything that might cause damage (like bullets) then I can make that happen easily and explicitly here.
  9. My default setting is anti-inheritance as a rule-of-thumb. It's fine to inherit pure interfaces, no real problems there, but once you start inheriting implemented methods, data-members, any multiple-inheritance whatsoever, etc. Then it's a slippery slope towards brittle code and poor reusability. There are times when it makes sense of course, but in my experience most people will tend to abuse it. In fact where I work we monitor any and all use of inheritance closely and we treat it with suspicion in code-reviews of newly written code. So with that in mind... Are you though? Inheritance is not the only way to share or reuse code. If that was the case then languages without any inheritance would be seriously screwed! One option is if you have a list of bounding volumes then you can just write 1 loop and check against all those volumes. That will work regardless of how many object types there are in the world (no inheritance needed either). This is similar to how a physics engine would do it. Alternatively, maybe for each of those 10 different object types you want 10 different behaviours for when there is a collision. That means 10 different implementations anyway, so why not just have 10 different loops!
  10. That probably works in your favour, there's a lot of people out there just itching to teach you bad OO in exchange for money! The only problem here may be the size of your World class so you might just want to carve it apart somewhat. For example If you find you have a bunch of code in there just for managing projectiles (spawning them, recycling them, detecting when they've hit something, etc) then factor all of that out into a ProjectileSystem and have World hold an instance of that. In principle though, having something that represents the 'active state of your game' along with update() methods that transition from state(t) to state(t+1) is a good design choice. Just don't overload it with other responsibilities such as rendering, audio, etc. If you treat or process player[0] differently then better to move it out of that array: Player player; Player ai[10]; No issues there! This is also fine! I do the same thing myself. There is no problem with this thinking. In fact there is a recent trend towards that kind of thing because uniformly processing an array of same-kinded objects in sequence is good for cache coherency and branch-prediction. When you start hitting up objects that are allocated all over the heap and invoking virtual methods on them all, then performance starts to take a hit. (Although Moore's Law is always hard at work to mitigate this!).
  11. WoA V - Afterparty/Judging thread

    Sure I'd be happy for that! (You'll probably do a better job at formatting it than I've done on my own site )
  12. WoA V - Afterparty/Judging thread

    Hi All! Here is my Post-Mortem of "Invasion of the Liquid Snatchers!" http://www.gilldave.co.uk/2017/08/week-of-awesome-5-post-mortem/ I've had a few people reach out to me about the fluid simulation and I've seen some of the community reviews speculating on how it works. So I've also written up a technical article dedicated to that: http://www.gilldave.co.uk/2017/08/fluid-rendering-with-box2d/
  13. WoA V - Afterparty/Judging thread

    @riuthamus I just finished watching your review stream. Very funny! Although I'm glad you enjoyed my game I think the highlight for me was watching you fail hard at @IYP's domino jumping game. Cheers!
  14. WoA V - Afterparty/Judging thread

    @slicer4ever I see a couple of reviews of my game by @Thaumaturge and @racsoth have been truncated. Is it possible to recover those?
  15. WoA V - Afterparty/Judging thread

    Good writeup! I'm intrigued by the sound of part2. Are you saying that all your game's state is functionally transformed?.. It's just maps and folds to arrive at the next frames state and not directly mutated? Mine might run for you as long as you have Java8: java -jar Game.jar