November 15, 2017
My latest project has been the basic game mechanics for herding humans with a dog. Why herd humans with a dog? You'll find out if I ever finish this game. I don't want to spoil it in advance.
My partner and I started this project earlier this year. In one short pair programming session, we built a dog (a box with a nose) that would animate to wherever you clicked or tapped on the screen.
My goal has been to get a simple version of the game play working that I can evaluate whether it's fun to herd humans with a dog and thus whether the rest of the game is worth building.
Here are the basic steps:
Draw a human (another box with a nose).
Tell the human to walk in a straight line.
Tell the human to turn 30 degrees when the human is within 10 pixels of the dog (a single corner of the dog, not anywhere on a rectangle, to keep things simple).
Then stop at this point because so far this sucks. The human spins like crazy when the dog is nearby because the human spins faster than it can move away from the dog.
[Sorry, I don't have a video of this. I must've deleted the code before I got too dizzy.]
Despite the stupid spinning human, I'm happy because I'd learned how to use threading macros in Clojure and was getting familiar with them.
A threading macro is a nicer way to write out a set of nested instructions. Here's a familiar example from math: ((3 * 2) + 1) is one math expression nested inside another. You start with evaluating (3 * 2) and then you take the result and add it to 1.
A threading macro writes this in the order it will be evaluated:
-> (3 * 2)
At first, I couldn't see how these chained together or the order of the arguments. Is it ((3 * 2) + 1) or (1 + (3 * 2))? There is a rule that determines this and you just have to learn it. Not so bad. Very useful.
[If you're interested in Clojure, you can read about it in this article about threading macros. I didn't show clojure code here because I think it would confuse most people I know – standard math notation is more familiar.]
I was a bit stumped about the spinning human.
I needed a system that would give the human a new direction only once, not repeatedly. I was stuck trying to imagine how to do this with the proximity check I was already using.
My partner suggested that I build a grid system. So I thought about it for a bit and it began to appeal to me. I did a little reading about grids from Amit Patel. (Someday I want to try a hex grid. Amit has great tutorials about building these.)
Since I'm working with a web browser, I'm already using the pixel grid. But it's been difficult because my characters are larger than a pixel; so which of its many pixels defines a character's location? Keeping track of those pixels gets complicated.
By building my own grid, I can group the pixels together, say in 40px by 40px boxes, and my characters can be located within the box. Now, the location of my human is simply a pair of coordinates on the grid (similar to the x,y coordinates of graphing equations). It's much simpler.
Having a visible grid made it easier for me to debug because I could see when things weren't lined up right.
Once I had the human and dog on the grid, I wanted the human to move about the grid without input from me.
First, I had the human move straight down the grid and stop when it reached the walls, the edges of the grid.
Then I added a check for the dog. If the dog was in the way, the human would stop.
Just moving straight down the screen is quite boring but it's a necessary step towards something more interesting.
There's no element of surprise in what I'd built so far. The human is so predictable and all you can do as the dog is stand in its way.
Someday, I want the human to have a path it is trying to follow but to start simple, I'm just going to have the human move around so I can try and herd it with the dog.
Movement 1: Look at the four boxes adjacent to the human, discard any that are off the grid or that contain the dog, then randomly choose one to move into.
The human walked all over the place, willy nilly. How do you herd something like that?
Movement 2: You are given a direction, move in that direction until you reach a wall or the dog, then turn to a randomly chosen direction.
This worked okay but my human tended to walk around mostly along the edges of the grid. I want the human to cross the middle sometimes.
Movement 3: Toss a 5 sided die and if it's a 1 (20% chance) then change direction, otherwise, follow the instructions of Movement 2.
Pretty passable. The human is a bit tricky to herd but the feeling of the interaction is beginning to take shape.
I want to simulate the way herding dogs can push other animals (or humans) in a certain direction.
The human shouldn't want to cross in front of the dog. It might be really cool to do some kind of reverse gravity stuff here but I need to start with something simpler. A dog-zone.
I created a line of boxes in front of the dog that are considered the dog-zone, a place that causes the human to change direction.
Now, the dog has more reach, enough to do some basic herding. With some imagination.
This is a good, basic prototype for dog herding. Prototypes should be bare bones, the least effort to get the most learning. I had bigger ideas but this does an excellent job of testing out a very simple version of the most important aspect of this game.
Is it fun to herd humans? I don't think this prototype gives me a definitive "yes" but it doesn't tell me "no".
I'm going to take a break from this and work on something else. The grid work in this project will probably be really useful for other games I want to build. A little time away will help me decide what to prototype next for dog herding.
Thanks for reading! Let me know if you have any questions or ideas for what to try next with the dog herding.