Saturday, May 26, 2012

Random Wandering Characters

After looking for some free artwork to start from, my brother and I found some nice sprites from these sources:

I took some plants for reiner's site and joined them into a single spritesheet, and my brother used some of the unit sprites from the Wesnoth community contributions as starting points.  I also drew some slopes and threw together some code to place the slopes and make the people wander around aimlessly.  Here are the results:

There are two known bugs:

1.  There is a somewhat rare bug that crashes the program if one of the characters has nowhere to go (for example, if they are surrounded by trees).  If that happens, just try running it again.  I'll fix that soon.

2.  The way that they negotiate slopes is kind of weird.  They don't really consider the direction of the slope when they choose to climb up or down, so you may see them hop up the bare side of a sloped piece, or sometimes it looks like they float in the air because of being "on top" of a slope...  yeah, it needs some work, but whatever.

Right now the male dwarf is the only one animated.  The rest have spritesheets ready for animation, but each character's frames are just copies of a single sprite:


Download the latest revision (As of this post it is - or you can always get the latest version on the project main page:

Run it, and report back how many frames per second you are getting, and what kind of hardware you have (cpu/memory/video).  I'm trying to figure out a way to improve the performance which is quite dismal since I've just been hacking this together. 

Pick one of the characters that still needs to be animated.  Use Gimp (or some other image editing app that supports transparency).  Follow the pattern shown by the male dwarf to complete the animation frames for your character. (note the direction that the dwarf faces on each row).

Extra Credit:

Monkey around with the code a bit.  Try opening src/entities.clj - you can arrange a different party by modifying the place-entities function.  You'll see that it creates a list of people by calling another function called "person" multiple times.  The "let" block at the top calculates the center of the map, but it isn't really necessary.  Look at the definition of the "person" function above, it takes a reference to the world, x/y coords, race, and gender.  So an example that places them in fixed coordinates instead of relative to the center of the world would be something like this:

(defn place-entities [wrld]
  [(person wrld 50 50 :dwarf :female)
   (person wrld 50 52 :dwarf :female)
   (person wrld 52 50 :dwarf :male)
   (person wrld 52 52 :elf :male)
   (person wrld 54 54 :elf :female)])

Also you could play with the "person" function.  Right now some of the values in there don't do anything, but walk-speed does work.  It is a number representing how many tiles they can walk per second.  It currently picks a random number between 1.0 and 1.5 like this: (+ 1.0 (rand 0.5)), but you could try erasing that expression and replacing it with a fixed number, or writing a different expression.

One thing that could be interesting is making the race determine the walk-speed, like this:

:walk-speed (cond
  (= race :elf) 2.0
  (= race :dwarf) 1.0
  (= race :human) 1.5)

The function "cond" takes pairs of conditions and corresponding expressions.  It finds the first condition that is true, and evaluates its expression.  In this case the expressions are simply numbers, but they can be more complex:

:walk-speed (cond
  (= race :elf) 2.0
  (= race :dwarf) 1.0
  (= race :human) (+ 1.5 (rand 0.5)))

 In that, the elves all get a speed of 2.0, the dwarves all get a speed of 1.0, but humans get a random speed between 1.5 and 2.0 

Don't get too attached to any of these functions, most likely as the project matures most the code will probably get rewritten and refactored to the point that it doesn't resemble the original at all.


  1. Oh, I forgot to mention that if you want to claim one of those spritesheets and fill it out, please say so in the comments here so that we don't duplicate work.

  2. I, Phillip will be finishing the dwarf sprite sheet and Matt with be working on the human sprite sheet.

    im almost done with the dwarf sprite sheet

  3. I finnished the dwarf sprite sheet and I will start working on the elven male sprite sheet soon.

  4. You might find (case) useful if you're checking against a constant. For example, you can replace this:

    (= race :elf) 2.0
    (= race :dwarf) 1.0
    (= race :human) (+ 1.5 (rand 0.5)))

    With this:

    (case race
    :elf 2.0
    :dwarf 1.0
    :human (+ 1.5 (rand 0.5)))

    Not earth-shattering, but it's a little bit easier on the eyes.

  5. Oh, nice! Duly noted. That will certainly simplify some of the logic in the existing code.