CSS Duck Hunt was the first game I submitted to 'CSS only' Mozilla Dev Derby over one year ago. It didn't win anything, but I was glad that I managed to develop a nostalgic game in a rather unique way. Since then, some people have asked me to explain how this game works.
At the beginning I had only a very general idea about what I wanted to accomplish. Everything except for the flying bird and the gun were supposed to be static images. After implementing the duck's movement and the process of shooting it, I started thinking about a more complex solution.
Birds and dog
Lets start with the basics. The core of this game is *just* flying ducks and the capability to shot them with the gun. I think it was the easiest part ;). I prepared an animated gif with a flying duck and styled it as a background of
Most of the game's elements are located inside
form. Each duck is de facto an invisible
input element, which is represented by its
label - with the mentioned gif as a background and crosshair cursor to imitate gun's viewfinder.
As you can see, the bird's labels are positioned absolutely and located outside, on the right side of the main
div within the game area. Each duck has its own styles with a unique flying animation.
The first bird will show up 6 seconds after the page loads and the 'GAME START' title disappears. The whole animation of that particular duck will last 3 seconds and during that time, the duck will move across the game area from certain points, which are specified in
duck_1 animation. In the last step (100%), the uncaught bird will disappear on the top of the game area, so that's why
opacity is set to 0. The browser will animate not only the movement of bird but also its smooth process of going invisible.
More information about CSS animations can be found on w3.org.
But what will happen when the user shots the duck? How does the shooting process work? It's very simple. As you remember, each bird is represented as an
<input type="radio"> element and its
label. When the user clicks on that
input will be checked, but from now on, it can be styled differently:
This mechanism reminds me of some kind of conditional statements in programming languages:
In my example this could be used as:
This is the core of this game, a simple conditional statement which is based on the
input's state. As you will see, this logic is used in the majority of the game's elements.
Very similar to this, is the dog's animation. Every duck has its own dogs represented as a
div elements with derisive or pleased static gifs of hunting hound:
~ selector we can select any
.dog_catch element, as long as they follow a checked
#duck_1. So it means that after shooting a duck, the pleased dog will start his animation and show caught bird.
However, if the duck hasn't been shot, the laughing hound will appear to make fun of you ;). To better understand it, I can use a conditional statement:
Ammunition and shots
Now let's continue with something a little more complicated. In the left bottom corner of the screen, there is place with ammunition, representing number of available shots. Each player has three chances to catch the duck, if he/she doesn't kill it, then the bird's label gets
pointer-events: none style, so from now on, clicking event won't be effective. It's something like
Each set of ammunition (3 for 1 duck) has to be shown after killing or missing each duck. I can't animate 'display' values so I figured out to replace it with
z-index property. When the new duck apears, the ammunition has to be visible, so each ammo's
div gets the highest needed
z-index value, which in this particular example is 6.
Ok, but how can we detect if the bird wasn't killed? My solution was to prepare 3 shot
inputs (1 per 1 shot/ammo) with
labels as big as the game area, which will be under (lower
index) the flying duck. When the new duck appears, related shots layer will get their
heights (remember that I can't animate
display values) values set to
230px. When the current bird dissapears, shot
layers's height has to be
0px to the end of the game. That's why I used
animation-fill-mode: forwards, according to the documentation, it will retain the computed values set by the last keyframe encountered during execution.
If the player misses the animated duck, a visible shot
input with highest
z-index will be checked and in the aftermath its nearest
+ selector) and ammunition will disappear. After clicking on the third shot layer, the duck's
label will get
pointer-events: none;, so it won't be clickable.
The whole shooting process can be explained as followed:
Score and points
On the right side of ammunition area, there is another panel with 'HIT' caption and ten white icons of ducks, which display missed or scored shots. Missed shots are represented with grey icons, scored with red ones.
It's very simple. By default the grey icon will be shown when duck flies away. In case of a succesful shooting of the bird, the grey icon will be replaced by red one. If not, the grey icon will stay as it was declared initially in its
animation property. To explain this simpler:
Counting points is a little bit more tricky. I prepared an image with a list of all possible points from 0 to 10000, which you can get during play and I used it as a background of
div.points_bg element. This
div is located inside
div.points_wrapper element, which acts like some kind of window, because its height is set to
14px so it can show only one row of score number. At the beginning of the game, only the lowest part of score image will be visible, so a player always starts with 0 points.
div.points_bg there are also ten
radio inputs which are checked, hidden (
display: none) and has
14px of height by default.
When the user clicks the duck's label, related
radio input will be marked as unchecked and new styles (
display: block) will apply. It will *push* other
block elements further to the bottom of page. So then
div.points_bg will be *moved down* by
14px and in the end, in window element (
div.points_wrapper) next row of score image will be shown.
Making games in CSS is neither easy nor is it an effective task. The whole game is just a long animation with a few *condition statements*, which depends on whether the user shoots a duck or not. I am also aware that my version of Duck Hunt is far away from perfection. Sometimes browsers have problems with detecting shots, and in addition, if a bird animates too fast, the player can shoot more that three times. Because of its *CSS nature* we can't pause the game, save it or restore it's state after reloading the page.
I don't think it's the new way of building HTML games, on the contrary, it isn't. But it definitely is the best way to learn something new and uncommon, to face problems you probably wouldn't have during development of a typical website. The most important thing for me is to try new, non standard ways of building software that will teach me how to open my mind to think differently and improve creativity.
A few months after my submision to MDN 'CSS only' contest, I found on Codepen a very similar Duck Hunt game. I recommend you read and analyze its code. Its author developed it a little differently than me. He also added few additional sprite based animaitons like: a running dog at the start of the game and the falling of a shot duck. His code opened my mind and gave me solutions, of which I hadn't thought of. It is very valuable reading!