Situation Normal: afu

A Platform Shooter

Don’t miss out! Wishlist us on Steam for exciting updates!

Join our mischievous, yet lovable agent on a cool and funny mission to save the world! In this gun-heavy platformer, you’ll navigate through hilarious challenges, hone your skills, and enjoy a side-splitting story. Get ready for an unforgettable adventure where the fate of the world rests in your hands!

Please note that the current look and feel are in the early stages and will evolve into something even more amazing in the final version.

Action

Action-packed platformer, a falling apart underground world, merciless mean goons and traitors. Don’t trust anyone. Switch our gun to have the full firepower.

Elements

We have all four elements in our game, the air slows your fall, fire burns every living being, earth will smash you if you are too slow, and water drips from old water pipes.

Fast Respawn

To make the whole trying and dying bearable we provide a quick respawn at the entrance. The rooms are short, getting longer and more packed as you progress, your skills improve and the weapons to pick are getting more and more powerful.

Juice Up Your Game

Jan Willem Nijman – Vlambeer – “The art of screenshake”

“Just fill your game with love and tiny details.”

Jan Willem Nijman

The 30 little Tricks from Jan Willem Nijman to Juice up your Game

I collected all the 30 tricks on the youtube video from Jan Willem Nijman in this article. Basically the trick name and a little tiny explanation. It’s actually just for my own convenience. But hey if it’s helpful for you, you are welcome.

1. Basic Animations & Sounds

Makes walking, jumping, picking up things, and shooting more fun. It doesn’t really matter if it’s high quality or simple. It makes it alive.

2. Lower Enemy HP

It’s more fun. It’s super frustrating if you need to shoot ten times on an enemy before he dies, makes your weapon feel feeble and boring.

3. Higher Rate of Fire

It’s a video game about shooting so put more bullets in there. You hardly can overdo it. Really. Just add more bullets. Give your weapon a higher fire rate.

4. More Enemies

Because of juice number 2 you need more enemies to shoot. It’s satisfying if they pop like popcorn.

5. Bigger Bullets

Don’t use realistically sized bullets as it looks so silly, make them big. The player wants to see those badass killer bullets. The weapon feels much more powerful.

6. Muzzle Flash

Just make the first bullet frame a circle. Very simple, very effective.

7. Faster Bullets

Slow bullets are lame, make it faster.

8. Less Accuracy

If your gun is not too accurate it looks more realistic and is more fun.

9. Impact Effects

Let the player know he hit something, don’t just remove the bullets. Like fragments falling off the wall if a bullet hits it or blood strains if a character gets hit by a bullet.

10. Hit Animation

Let the player know that he damaged the enemies. The simplest trick is to make the enemy flat white for a fraction of a second.

11. Enemy Knockback

But knockback force if you hit enemies. Your weapon feels so much more powerful and makes it more realistic from a physical point of view.

12. Permanence

Don’t remove dead bodies, it helps you remember if you come back in a room with a huge pile of dead bodies in it.

13. Camera Lerp

Smoother cameras make the character feel more in motion.

14. Camera Position

Your camera focus on important things in your video game. This is one of the hardest part and takes a lot of trials and testing.

15. Screen Shake

This is an important rule add screen shake on explosions it looks so much more powerful than without.

16. Player recoil

Shooting feels much more powerful with recoil on the player and you can use it to avoid that player do not shoot all the time as it has a drawback.

17. Sleep

If you set a deadly hit sleep for about 20 ms, you can hardly see that but it puts more meaning in that action.

18. Gun Delay

Make the character and its props look like they have weight/momentum when moving.

19. Gun Kick

Add recoil to things that would have it.

20. Strafing

If you shoot and change the direction the shooting will be still in the same direction until you stop shooting to turn around the gun.

21. More Permanence

Put ejecting shells in your game and make them permanent. It also helps to remember that you were in that room and that you did shoot a lot.

22. More Bass

Add bass to your shooting and explosions they get much more meaning and power.

23. Super Machinegun

Try ridiculous variations on how shooting performs, maybe three bullets a time, go crazy.

24. Random Explosions

That is a funny one but it puts more action in your game if you put some random explosions to your game when you shoot enemies, don’t overdo it.

25. More Enemies

You tuned up your gameplay so that you got the feeling you do have not enough to shoot at.

26. Even Higher Rate of Fire

Because it is more fun to shoot

27. Even Higher Rate of Camera Kick

Make your camera kick in the direction of the action/shooting.

28. Bigger Explosions

Seriously, who doesn’t like explosions?

29. Even More Permanence

Put some dust after your gigantic explosions and let it fade slowly.

30. Meaning

Let your player win or loos. It’s an obvious one.

Level Design Patterns

Intro

A collection of level design patterns, which might be helpful for someone.

Patterns

Safe Zone

A safe zone is a pattern that refers to an area where the players are not exposed to danger. A Safe Zone gives the player the possibility to analyze the surroundings and plan their next actions. A safe zone is especially important at the starting point of the avatar.

When you enter a level you always should be in a safe zone where nothing attacks or threatens you. No enemy should be able to enter the safe zone, it’s upon the player to leave the safe zone.

When you enter a level you always should be in a safe zone where nothing attacks or threatens you.

My rooms for All Fucked Up start always in a safe zone means no hazard or enemies will enter that zone. I will use leap of faith jumps for storytelling and therefore I will soften the strict rules for the safe zone a little bit. It will be guaranteed that there is no hazard or enemy at the landing point which would kill you instantly. The player should have enough time to react and have the necessary amount of ammunition and weaponry. As well a leap of faith jump will never be at the end of a room or level. Combined with the fast and endless respawn this should be acceptable.

Guidance

Guidance is a pattern that refers to the use of non-verbal game elements to guide players in an intended direction. Guidance is especially important for explorative games.

Guide the character through the level shape from the start to the end. Besides gangways, shafts, and entry/exit doors, also enemies or collectibles can be used to guide the player through the level. But as well cracks in a wall can indicate that with a certain amount of force you can destroy that wall to enter a new room.

My levels have entry and exit points in rooms that are not too big. I plan to have hidden entries to bonus rooms and those I have to somehow decorate with a guidance pattern like destroyable blocks and walls.

Branching

Branching is a pattern that refers to providing the players with multiple paths to reach their goals.

Not just one possible path but a couple of possible paths. You can have difficult but faster ways through for experienced players or simpler but slower paths for beginners.

Alternative ways to kill your enemies.

For All Fucked Up I recently found another way of branching, not in the sense of alternative paths but alternative ways to kill your enemies. I started to work on a level with falling blocks and the blocks start to shake for a sec and fall down as soon I’m underneath them. If I can lure my enemies underneath the falling blocks and manage to save my ass in time I can kill the enemies without a gun. I can now think of more ways like this, like shooting fuel barrels near enemies and letting them be blown away.

Foreshadowing

Safely introduce a new enemy for example, by showing the new enemy in a safe way before it attacks you.

There are different ways to introduce new enemies, threats, or hazards. One way is to combine Foreshadowing with the Safe Zone pattern, the player is placed in a safe spot where he can observe the new enemy, threat or hazard and plan his action. Even new possibilities could be introduced that way, like the trampoline or the moving platform in celeste.

One way is to combine Foreshadowing with the Safe Zone pattern.

In All Fucked Up fast respawn after death, endless lives, and short levels make it quite easy to get knowledge of new enemies and threads. All my threads and enemies are introduced isolated at the beginning of a room, this gives the player an easy way to figure it out as he gets respawned right away after he gets killed by the new threat or enemy.

Layering

Layering is a pattern that refers to combining multiple game objects to create a new experience or challenge.

This pattern is most often combined with Foreshadowing. For example, introducing a new enemy separated and then combining them in a flock or together with already introduced enemies, threats or hazards. This gives the game new challenges.

In All Fucked Up I combine a lot the Foreshadowing and the Layering pattern. One example are the falling blocks. Whenever the player is underneath a falling block the block starts to shake for a sec and fall then down and smashes everything underneath also enemy entities. The player can combine the falling block in his tactic to smash following enemies.

The player can combine the falling block in his tactic to smash following enemies.

Pace Breaking

Pace breaking pattern refers to purposely changing the dramatic arc of the game.

Changing the surroundings or the sound for example when a boss enters the scene. Or to slow down the game for short moment to give the player time to prepare for the next wave of enemies.

In All Fucked Up I have some silent moments when finishing a room by adding a longer corridor where you just walk. But as well by environmental sound change. For example in the hall of hydraulic hammers you hear the hydraulic sound when the hammers lift and the boom noise when the hit the ground.

Proxy

Proxy is a pattern that refers to indirectly triggering an action.

Switches that open a door or unleash an event. The switch is not directly connected. Or an invulnerable enemy which I can only be kill by destroying a power supply. Fuel barrels which explode if you shoot at it. And more.

In All Fucked Up I introduced smart cards which you need to open the door to the next room. Those smart cards are carried by some of the enemies. This also should give the player the necessity to shoot those fuckers and not just try to avoid them.

Privileged Move

Privileged Move is a pattern that refers to the fact that not all objects do have the same impact on different objects.

Bullets from enemies do not damage enemies themselves only the player. Or an acid ground does not affect enemies but the decrease the players health.

For All Fucked up bullets from enemies only damage the player never other enemy entities. In case I introduce co-op mode I will do the same for the players that player bullets only damage enemies never the other player.


I get a small commissions for purchases made through the following links, I only have books in this section which I bought myself and which I love. No bullshit.

References

https://www.gamasutra.com/blogs/AhmedKhalifa/20190610/344344/Level_Design_Patterns_in_2D_Games.php

https://eledris.com/design-2d-platformer-levels/

https://www.gamasutra.com/view/feature/132649/the_case_for_game_design_patterns.php?print=1

https://www.youtube.com/watch?v=4RlpMhBKNr0

Lens #18: The Lens of Passion

The lens #17, the lens of passion from The Art of Game Design: A Book of Lenses written by Jesse Schell. If you buy books through the links in this article I will get a small commission for that and no extra cost for you. The book is great and I think a must-have for every game designer.

This lens is about my passion for my game. I should ask this from time to time myself, like how do I feel about my game with the following three questions:

  • Am I filled with blinding passion about how great this game will be?
  • If I’ve lost my passion, how can I find it again?
  • If the passion isn’t coming back, shouldn’t I be doing something else?

I’m not always blinding passionate about my game, I especially have this reality check when I try to design a new level. It’s quite a lot of work to make a level great and joyful and sometimes reality kicks in and I’m like nah nope no not cool. But then I fiddle with it and try different styles and make it more narrow and more intense and I feel this joy and passion and I’m like yes it’s good it’s great.

So I do this reality check almost automatically and see if I still like what I do and still like what I see and play. COVID-19 didn’t help too much for the play testing something I would like to do on game conventions. Just to see if others would also like this little hero and his adventure.

If I lose the passion I usually find it again. Sometimes I just need a break or I need to look at it from a different angle or just make some fun prototypes and see if it would fit which is most of the time not the case. I also work every single day except Sunday on the game. It doesn’t matter if I just write down some ideas, make some sketches, brush up a level, improve a little bit the game-mechanic, or even remove something from the game. This everyday habit helps me a lot to keep the drive and the passion for my own game. And it is quite amazing to see how it develops when I compare it to the early stages and now.

The last question of what to do when I can not find my passion for my game. Well, I probably would then just finish it somehow but would not put too much work into it.


I get a small commissions for purchases made through the following links, I only have books in this section which I bought myself and which I love. No bullshit.

Cinematic Camera

I Want

In the process of my game, I started to think about an advanced camera system. I wanted to define camera bounding boxes in which a certain type of camera is active. But this was not enough for me as I wanted them stacked. And I wanted to attach these camera bounding boxes as well on moving targets like enemies.

Why?

Well, I was thinking of a cool intro where the hero falls down a deep hole. I wanted to zoom close in the beginning and zoom out in the middle part and zoom in on the landing. Furthermore, I wanted to overlay camera bounding boxes to zoom in on certain objects or when I enter or leave a room or level. I was as well thinking of a centered camera for a squared room to see everything in it and as soon I leave this room I wanted my follow camera back. So it’s complicated. And then I thought OK would be cool to have a zoom-in if enemies approach me to give that more focus and this camera bounding box has to follow the enemy… of course.

So it’s all about the cinematic effects I guess.

How?

I think the best is a visualization of my idea.

I tried to describe it in the level itself. The long light blue bounding box across the full room contains a follow camera and is on the bottom of the stack that’s why it’s numbered with a ‘0’. The middle camera bounding box is darker blue and is on top of the following camera and contains a centered camera, which means inside this box the camera will not follow the character but point to the center of the bound box. This box has the priority ‘1’ and is on top of the priority ‘0’. On top of this, we have a yellow bounding box with a zoomed-in centered camera with the priority ‘2’. The higher the number of the bounding box camera the higher its priority. On the right, we see a yellow bounding box with priority ‘1’ which is higher than ‘0’ with a follow camera with a different zoom.

In Action

Now in action

This is pretty neat isn’t it?

Behind the Scenes

OK, I try to explain it with some pseudo-code. Let’s assume we have all the camera bounding boxes and their camera type. Furthermore, we can load these camera types on the fly.

public void updateCamera(float tpf, Vector2 playerPosition) {
    stream()
            .filter(cam -> cam.getAabb().contains(playerPosition))
            .sorted(Comparator.comparingInt(CameraAabbData::getOrder).reversed())
            .findFirst()
            .filter(cam -> cam.getMyCamera() != currentCam)
            .ifPresent(cam -> {
                LOG.debug("Enter camera area with cam data type: " + cam.getType() + " properties: " + cam.getProperties());
                if (cam.getMyCamera() == null) {
                    cam.setMyCamera((MyCamera) feather.instance(cam.getType()));
                }
                currentCam = cam.getMyCamera();
                currentCam.setAabb(cam.getAabb());
                currentCam.setProperties(cam.getProperties());
            });
    currentCam.update(tpf);
}

So in short

  • We check if the player is in the camera AABB
  • We sort the camera AABB due to its order
  • We just take the first, which means the top one (it’s ordered)
  • The next check is to avoid that we set the camera properties on every frame
    • And if there is a camera AABB we check if we already instantiated the camera (caching)
    • We set the current camera and its properties
  • Last but not least we update the current camera on every frame
    • A lerp function smooths the switch from one zoom to another
    • A follow camera for example updates its position depending on the character’s movement
    • Update other dynamic features of the current camera

The object “cam” is just a data holder and often referred as a DTO.

Kanban + SCRUM

I did some micro blogging on twitter about Kanban and SCRUM

It’s actually a fun topic and executed well can bring a team together to work on a project. It involves the whole team and can not just be dictated that is a very important part. And this involvement also increases the team spirit as everybody gets a voice to improve the process.

Kanban over SCRUM

I prefer Kanban over pure SCRUM because it solves the problem of half-finished tickets. SCRUM usually plans a set of tickets for a sprint and developers tend to try to finish all tickets within a sprint at all costs because it was promised to the customer. The problem with that is that either you skip unit tests to get stuff done in time or you do only 80% of the task which leads to follow-up tickets. It’s a lot of work to keep an overview and needs in my opinion a fully committed SCRUM master.

Kanban, on the other hand, does not plan a specific amount of tickets for the next sprint but over time you get an understanding of how long on average a ticket needs until it is done. This gives the team more freedom, leads to better quality, and reduces the stress on developers to finish tickets on an estimated time. Often the estimation is not even done by the developer who takes the ticket! And a ticket is done if it’s done 100% including testing. And Kanban has less overhead compared to SCRUM and fewer rules.

SCRUM Particles

But I like the idea of dailies, planning, and grooming from SCRUM. And always take care that a meeting has an outcome and is as short as possible. I don’t like meetings in general as usually it’s just chatting and complaining but not outcome-oriented. It needs a person which moderates a meeting and keeps the meeting on the topic at hand. It’s for example totally pointless to waste time talking about your code problems in a daily meeting, just state you have a problem with your code and then figure out who could help you. All the meetings have a specific purpose and the moderator should take care that all participants stick to that purpose and stop discussions that have to take place elsewhere.

Daily Chat

A daily should consist of the three statements

  • What did I work on before this daily
  • What do I work on after this daily
  • State the blockers I have

The first two points are to make sure everybody in the team knows what topic you are working on and the last point is about your problems and defines who can help you to eliminate them. The meeting should be quick and concise. Nothing is more boring than following a discussion you are not interested in. Imagine you also might have your game designer in your daily among some developers. If developers start to explain in detail their problem they waste the time of the game designer. I was in so many meetings where two people started to discuss a topic only they did understand and where I just thought ok I would like to go back and work now. And I’m sure I’m not the only one.

Planning the Sprint

I like the idea of sprints, but in the Kanban world, this can be more dynamic than in the SCRUM world. This means the stakeholders can influence the backlog stack by sorting it and the todo column of the Kanban board as well. This gives them a huge benefit in short-term decisions. The stakeholder can not influence tickets that are already taken by a developer except having serious reasons.

To make that work the developers must work the Kanban board from right to left and from top to bottom. Cherry picking is not allowed. This as well does have a huge benefit as the unwanted uncool tasks are evenly distributed on the team. It’s also very self organizing if all developer follow that rule.

The time a ticket needs from left to right on the Kanban board is at the beginning not clear. After 4 – 8 weeks it becomes more clear how long a ticket needs in average till it is done and then you also can calculate how many tickets you can do in one sprint of lets say 3 weeks and the stakeholders can see on the board what is in the next release for sure and what might be in the next release. Of course there will always be the case where a certain ticket just takes much longer than the average but will be less likely the longer you do Kanban.

Grooming the Backyard

This is the most valuable meeting in my opinion. It is important that all developers participate. The ultimate goal is to make sure that in the end, everybody understands the scope of the tickets you groom in the backlog. The moderator of the grooming meeting documents all the findings, howtos, and input them into the description of the ticket. As we always play planning poker on those tickets and you do not estimate the time but the complexity. There might be a ticket with a complexity of 0 or 1 but will take 2 weeks because it is a lot of work. You still can split such tickets if you know it might be a lot of work.

Hearts, Diamonds, Spades, or Clover

The planning poker can be played with physical cards or online like this one https://scrumpoker.online/. We used usually the schema Fibonacci series 0, 1, 2, 3, 5, 8, 13, 20, ?, or coffee. After a while it was clear to us that tickets which have complexity of 8 or more needs to be break up, but I think this is highly related on how a team understands those numbers. For us this became clear over the course of 3 plannings. We used the retrospective meetings for this kind of fine tuning. But it reflects the understanding in the team. If somebody put a high number which was higher than the rest he/she did explain his concerns as he/she might found something the rest of the team did not think of.

Retrospective

The retrospective meetings were mainly to improve the process i.e. how to work on the Kanban board when to do a review of a ticket, or if we need an additional column on the Kanban board among many more.
Usually, every member of the development team did prepare a set of positive points on what went well and a set of negative points on what went not so well. For the negative points, he also prepared a possible suggestion on how to improve it.

Kanban Board

The Kanban board should have at least a Todo, On Hold, In Progress, On Review, Verify, and Done slot. Every slot should be limited, we used the retrospective to figure out the best limits. The Todo should always have enough work till the next grooming session. The On Hold slot is for every ticket where people are blocked and need feedback or input. All tickets where somebody was working on are in the In Progress slot. If you pushed your stuff to your source repo you also moved the ticket into the Review slot. In case the reviewer did find issues you had to put back the ticket into In Progress. And if you finally managed to solve the ticket you had to put it into the Verify slot. The Verify slot stands for you have to install the product, game, plugin, whatever it is, and try out if it works or not. And it’s important that not the same person who worked on a ticket did the verification. The verification was a very good instrument to distribute the know-how in the team.

As I said in the previous chapter already, you work from right to left and from top to bottom on your Kanban board. So whenever I finished a task or came to work in the morning, I went on the board and had a look if there are tickets to verify. If yes I took one ticket after another and verified them until there was no work left for me. If everybody does it, it’s unlikely that I have to verify a lot of tickets in one go. Usually one or max two tickets. Then I go to the next slot and review all tickets in that slot. And if I was done with that I took my ticket from yesterday in the in-progress slot. If there is no ticket in the progress slot I took a look at my on-hold tickets. And if even there everything was done I mechanically took the topmost ticket from the todo slot and put that in progress. I do not even look what ticket this is, I just take it and deal with it.

Meetings

Keep them short.

Barrel Distortion Lens

After the fisheye lens shader turned out to be too extreme for All Fucked Up I looked for other lens shaders and found barrel distortion shaders which makes a similar effect like a fisheye but less extreme. It just widens up the lens. Looks totally cool. I got the best and simplest shader for barrel distortion here on github. It’s in a different format than I need it for jMonkeyEngine but was quite simple to adapt it.

I always start with the part I need to embed the shader in the code and as I do not make anything fancy or parametrized it’s fairly straight forward

package ch.artificials.bubble.system.mvc.view.post.filter;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.post.Filter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
public class BarrelDistortionFilter extends Filter {
     public BarrelDistortionFilter() {
         super(BarrelDistortionFilter.class.getSimpleName());
     }

     @Override
     protected Material getMaterial() {
         return material;
     }

     @Override
     protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
         material = new Material(manager, "MatDefs/Post/BarrelDistortion.j3md");
     }
}

The referenced MatDefs/Post/BarrelDistortion.j3md holds the references to the shader code and also defines possible parameters which we want to hand over from code to the shader code.

MaterialDef Toon {
    MaterialParameters {
         Int NumSamples
         Int NumSamplesDepth
         Texture2D Texture
         Color Color        
    }
    Technique {
         VertexShader GLSL100:   MatDefs/Post/BarrelDistortion.vert
         FragmentShader GLSL100: MatDefs/Post/BarrelDistortion.frag
         WorldParameters {
         }
         Defines {
             RESOLVE_MS : NumSamples
         }
    }
}

The vertex shader code is very simple and just hands over the tex coordinate and the position

import "Common/ShaderLib/GLSLCompat.glsllib"
attribute vec4 inPosition;
varying vec2 Vertex_UV;
attribute vec2 inTexCoord;
void main()
{
    vec2 pos = inPosition.xy * 2.0 - 1.0;
    gl_Position = vec4(pos, 0.0, 1.0);    
    Vertex_UV = inTexCoord;
}

The barrel distortion logic is in the fragment shader code and looks like this

import "Common/ShaderLib/GLSLCompat.glsllib"
import "Common/ShaderLib/MultiSample.glsllib"
uniform sampler2D tex0;
varying vec2 Vertex_UV;
uniform sampler2D Texture;
const float BARREL_DISTORTION = 0.25;
const float rescale = 1.0 - (0.25 * BARREL_DISTORTION);
void main()
{
    vec2 tex0 = Vertex_UV;
    vec2 texcoord = tex0 - vec2(0.5);
    float rsq = texcoord.x * texcoord.x + texcoord.y * texcoord.y;
    texcoord = texcoord + (texcoord * (BARREL_DISTORTION * rsq));
    texcoord *= rescale;
    if (abs(texcoord.x) > 0.5 || abs(texcoord.y) > 0.5)
         gl_FragColor = vec4(0.0);
    else
    {
         texcoord += vec2(0.5);
         vec3 colour = texture2D(Texture, texcoord).rgb;
         gl_FragColor = vec4(colour,1.0);
    }
}

And the lens effect in action looks like this


I get a small commissions for purchases made through the following links, I only have books in this section which I bought myself and which I love. No bullshit.

Fisheye Lens Shader

I always wanted to write some shaders and I always procrastinated it. But now I did it. Finally. I got a good source for a fisheye shader and I adapted it for the jMonkeyEngine which was not too hard.

Fisheye.j3md is the thing which holds everything together. This is jMonkeEngine specific and may look different for your game engine you use. This just holds the two shaders files together and it’s the place where you can specify the parameters which you need if you want to handover values from the code the shader pipeline.

MaterialDef Toon {
 MaterialParameters {
     Int NumSamples
     Int NumSamplesDepth
     Texture2D Texture
     Color Color
   }
   Technique {
     VertexShader GLSL100:   MatDefs/Post/Fisheye.vert
     FragmentShader GLSL100: MatDefs/Post/Fisheye.frag
     WorldParameters {     }
     Defines {
       RESOLVE_MS : NumSamples
     } 
   }
 }

Fisheye.vert is the vertex file and here I had to adjust the fact that jMonkeyEngine hands in the position as inPosition and as well the texture coordinate as inTexCoord. The example I got was for another game engine where the input UV was a vec4. To avoid having to change too much I packed the vec2 inTextCoord (which is basically the same thing as the vec4 UV) into a vec4 even tho in the end the fragment file will only use xy means the vec2.

#import "Common/ShaderLib/GLSLCompat.glsllib"
attribute vec4 inPosition;
varying vec4 Vertex_UV;
attribute vec2 inTexCoord;
void main() {
     vec2 pos = inPosition.xy * 2.0 - 1.0;
     gl_Position = vec4(pos, 0.0, 1.0);    
     Vertex_UV = vec4(inTexCoord, 0.0, 0.0);
}

That’s it the rest I took over untouched from here.

Fisheye.frag

import "Common/ShaderLib/GLSLCompat.glsllib"
import "Common/ShaderLib/MultiSample.glsllib"

uniform sampler2D tex0;
varying vec4 Vertex_UV;
const float PI = 3.1415926535;

void main() {
  float aperture = 178.0;
  float apertureHalf = 0.5 * aperture * (PI / 180.0);
  float maxFactor = sin(apertureHalf);
  vec2 uv;
  vec2 xy = 2.0 * Vertex_UV.xy - 1.0;
  float d = length(xy);
  if (d < (2.0-maxFactor)) {
    d = length(xy * maxFactor);
    float z = sqrt(1.0 - d * d);
    float r = atan(d, z) / PI;
    float phi = atan(xy.y, xy.x);
    uv.x = r * cos(phi) + 0.5; uv.y = r * sin(    phi) + 0.5
  }  else  { 
    uv = Vertex_UV.xy;
  }
  vec4 c = texture2D(tex0, uv);
  gl_FragColor = c;
}

There is a little bit of Java code involved to be able to add the filter as post filter. This is most likely different for your game engine.

package ch.artificials.bubble.system.mvc.view.post.filter;

import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.post.Filter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;

public class FisheyeFilter extends Filter {
    public FisheyeFilter() {
        super(FisheyeFilter.class.getSimpleName());
    }

    @Override
    protected Material getMaterial() {
        return material;
    }

    @Override
    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
        material = new Material(manager, "MatDefs/Post/Fisheye.j3md");
    }
}

And this is the effect. Not too nice but a good starting point to play with this shader.


I get a small commissions for purchases made through the following links, I only have books in this section which I bought myself and which I love. No bullshit.

Camera System

Finally, we started to think about the camera logic. How to move the camera relatively with the character we control in a way that the player gets the most out of it. There was not so much to find but this Gamasutra article from Itay Keren helped me a bunch. I know at least have a clue what I’m talking about. And a tweet about the camera system of qomp from Stuffed Wombat inspired me to do something similar.

Head Into

The problem I had was that the camera was always following the character which made it quite a challenge to watch and might cause motion sickness.

Camera Systems

Lerp Camera

I found on this page a good lerp implementation which I use as well. It solved almost all my jitter problems I had with my own lerp camera implementation. It’s pretty cool and worth a read.

Projected-Focus

This is a projected-focus camera that looks a bit ahead of the player while moving.

The initial camera code was very straight forward and simple

Vector2 velocity = new Vector2(player.dyn4jBody.getLinearVelocity());
camera.setLocation(new Vector3f((float) (player.location.x + velocity.x / 2),
            (float) (player.location.y + velocity.y / 2), 0));

I just used the velocity to catch up with the player and look in the direction he currently moves. This was needed because the underlying lerp-smoothing logic, will let the camera fall back on high speed like a free fall so a velocity-based look ahead seemed the best solution.

This camera always follows the player. This can be annoying if we do wall jumps where the character always moves in and out. I guess after a while people will get sick.

Camera-Window But Keep Projected-Focus

The goal was to reduce the camera movement to a minimum and focus on the direction we move. For this, I implemented a combined camera which was camera-window and projected-focus. This means inside a window the character can move while the camera does not move. As soon the player touches the edge of the virtual window the camera starts to follow and look ahead.

You can imagine that this code would be much more complicated. As well I only wanted the window for the horizontal move but not for the vertical moves (for the vertical moves I do not yet have a clear plan). After a few trials and some refactoring later it was clear that I will use a behavior tree to implement the much more advanced logic for this camera-window and project focus logic.

Sequence<CameraBlackBoard> checkStillMovingX = new Sequence<>();
checkStillMovingX.addChild(new Invert<>(new DetectPlayerMoving(Axis.X, 0.1f)));
checkStillMovingX.addChild(new SetCamFollowing(Axis.X, false));

Selector<CameraBlackBoard> shouldFollowX = new Selector<>();
shouldFollowX.addChild(new Invert<>(new DetectPlayerCamRange(Axis.X, 4)));
shouldFollowX.addChild(new DetectCamFollowing(Axis.X));

Sequence<CameraBlackBoard> followX = new Sequence<>();
followX.addChild(shouldFollowX);
followX.addChild(new SetCamFollowing(Axis.X, true));
followX.addChild(new CamFollow(Axis.X, 0.75f));

Parallel<CameraBlackBoard> control = new Parallel<>(Parallel.Policy.Sequence);
control.addChild(new AlwaysSucceed<>(followX));
control.addChild(new AlwaysSucceed<>(new CamFollow(Axis.Y, 0.5f)));
control.addChild(new AlwaysSucceed<>(checkStillMovingX));

bh = new BehaviorTree<>(control, bb);

I had to distinguish between horizontal and vertical movements as this is decoupled. I applied the window logic only for the horizontal movement (the x-axis) which was even with a behavior tree implementation a bit tricky to achieve. The vertical movement is in parallel to the horizontal movement but strictly following. The last sequence checks if the character stopped so we can stop following. We following a character if he touches the window border and keeps following until the character stops. The behavior tree forces us to break the code up into simple tasks which makes it a piece of cake to implement and test.

This reduces the movement of the camera while the character walks back and forth within a specified window. I still have to figure out and fine-tune what is the best window size. It also looks ahead in the direction we move.

There is an underlying camera that does lerp-smoothing to make it softer on direction changes and stops. The underlying camera also does have a kickback function for the camera kickbacks if we fire a weapon to give the weapon more boom. I use the kickback vertically as well for hard landings which gives it a bigger impact.

Zoom

I added a simple “Zoom” property and extended my cameras with a setProperties to set any kind of property and every camera implementation can get whatever property it understands and apply it. The zoom is simply the distance to the character as I decided to use a perspective camera instead of an orthogonal camera. The game also looks a bit more interesting with a perspective camera.

Camera Switch

I implemented as well a simple camera switch logic. With tilded I have an object group where I define the camera area, the name of the camera type, and the camera properties like “Zoom”.

        cameraContainer.stream()
                .filter(cam -> !cam.entered)
                .filter(cam -> isPlayerInCam(cam))
                .findFirst()
                .ifPresent(cam -> {
                    cam.entered = true;
                    myCamera = (MyCamera) feather.instance(cam.type);
                    myCamera.setProperties(cam.properties);
                });
        
        cameraContainer.stream()
                .filter(cam -> cam.entered)
                .filter(cam -> !isPlayerInCam(cam))
                .findFirst()
                .ifPresent(cam -> {
                    cam.entered = false;
                });

That’s it so far. I enjoyed the journey 🙂


I get a small commissions for purchases made through the following links, I only have books in this section which I bought myself and which I love. No bullshit.