EndlCss Runner

Concept

This was my first entry into the 10k Apart competition and really allowed me to get to grips with css animations. I knew I wanted to make a game and I was originally thinking a bit smaller and more doable such as tic tac toe with an AI. After doing a bit of research I realised this wasn't an original idea and that there's already a great example of this by Nisha Batra.

I realised I'd have to think a bit bigger and aimed towards making a recreation of Flappy Bird. After some playing around I concluded that it wasn't actually possible to recreate this in pure CSS due to the physics of the bird and the unpredictable nature of the player input. I needed to make something where I could reliably control the player input and, after playing around for a long time, concluded it would be possible to make an endless runner game. You can play the finished version here.

How it works

Just from playing the game it can be difficult to work out what's going on, but it's actually fairly straight forward. There are five radio buttons which control the player movement: two for jumping, two for sliding and a radio button for the initial running state. Each time you select the correct radio button you will reset a global animation timer which is set to show the Game Over screen every second. Two radio buttons are required for each of the moves in case you're required to jump/slide twice in a row. You can't detect a reselect of the same radio button so they're toggled hidden/visible alternatively so that you're selecting a different radio button each time.

If you jump when you are meant to slide, or vice versa, then you will actually be clicking a hidden checkbox which overlays the incorrect button. This moves to cover the incorrect button at the same rate as the obstacles appearing on the screen. The pseudo randomness of the obstacles is done by having 100 elements going left on a loop by increasing the animation delay, with the type determined by using various nth-child values (2n, 3n, 5n and 7n). This obviously isn't actually random, but does enough to prevent you being able to detect a pattern.

@for $i from 1 through 101 {    
  &:nth-child(#{$i}) {
      animation-delay: #{$i  * $speed};
      z-index: #{$i + 5};
  }
}

The score handling was a bit trickier and is the bit I'm least happy with. I was originally hoping to use the little known css counters to increment by one every time a radio button was selected, but it turns out they don't actually work that way. Instead I had to create a timer which animated through the numbers at the same pace as the obstacles. This works fairly well, but there is no way for me to automatically stop the timer if you don't press anything. Instead I had to make a checkbox appear when the timer runs out; checking this causes the timer to stop - far from ideal.

The animation of the ninja itself is actually inconsequential to the game but is just handled by setting a new animation to each of the various body parts each time the corresponding radio button is checked. Unfortunately, I had to repeat each action animation twice (jump1, jump2 etc.) for the duplicate radio buttons. Setting the same animation again doesn't restart it unfortunately.

#jump1:checked {        
  &~ #canvas #dude {
      animation: jump $personSpeed * 2,  $personSpeed * 0.5 bob1 $personSpeed * 2  alternate linear, $personSpeed death1 $speed * 1.25 linear;
      animation-fill-mode: forwards;
      .larm {
          animation: jumplarm1 $personSpeed * 2, $personSpeed * 2 arm1 $personSpeed infinite alternate linear;    
      }
      .rarm {
          animation: jumprarm1 $personSpeed * 2, $personSpeed arm1  $personSpeed * 1.5 infinite alternate linear;    
      }
      .rleg {
          animation:  jumplleg1 $personSpeed * 2,  $personSpeed leg1 $personSpeed * 2 infinite alternate linear;    
      }
      .lleg {
          animation:  jumprleg1 $personSpeed * 2, $personSpeed leg1  $personSpeed * 1.5 infinite alternate linear;
      }
  }
  &~ #jump1button {
      display: none
  }
}

Resetting the game is surprisingly easy as setting 'display: none' then back to 'display: block' completely resets the animations.

You can play the finished game here, or view the source on my Github. If I've missed anything, or you simply want to get in touch feel free to comment below or contact me on Twitter.