Friday, October 28, 2011

roguelike tutorial retrospective

Time for a retrospective! I think retrospectives are probably the most important thing a developer could do since reflecting on what you've done is the difference between someone with 10 years of experience and someone with 1 year of experience repeated 10 times. There are as many different styles of retrospectives as there are teams who do them but I've found that these few questions are usually good enough for me.

What does this retrospective cover? For this roguelike tutorial I tried a few new and different things; the most obvious one being that this was a series of blog posts. This is also the first time I had something of a magic system. There were also a few tweaks to how I normally code things. The roguelike itself is not meant to be a complete and fun game - it's just a basic example of a few different things that may help some roguelike developers - so I'm not going to review the game as a game.

What worked well? As far as code goes, the Screen interface worked really well. Having goblins pickup and use items is neat, as is learning the identity of things by watching others use them. As far as a tutorial goes, I think it can be useful for some people. Even though like there are, at most, a handful of people who read each post, I did get more feedback than I expected, including a reference to behavior trees - something I didn't know about. I'm also pleased that my posts were a mix of code, references to other sources, and some of my own thoughts about why I'm doing what I'm doing. It's not as authoritative as saying "THIS IS THE ONE TRUE AND RIGHT WAY TO DO THIS", and some of it was probably a bad idea, but I've always found that the most instructive guides show the uncertainties and mistakes we run into and how to deal with them.

What needs to work better? One thing I've learned about building something via blog posts is that refactoring is very difficult to show; it's easy to show new code to add, especially if you have small classes and methods, but I don't know of any easy way to show removing, rewriting, and refactoring. This means that the final code, although mostly small pieces, could use a lot of cleanup work and this isn't representative of my best code and may even be a bad example. Oops. Using no globals was a laudable goal but lead to some things I'm not to happy about, like the path finder using a collection of points rather than a fixed size 2D array since I didn't have an easy way to tell the path finder what the map size is. There are a few minor annoyances with the code, like the creature class becoming a behemoth, but most of that is from adding features without refactoring. The spell-related code is quite ugly and clunky too - mostly because I'm not familiar with writing magic systems and couldn't refractor so it was just charging ahead with little idea what I was doing.

What should be done differently next time? I still think a series of blog posts can make a good tutorial but it needs something that makes it easier to show refactoring - perhaps putting everything on github and discussing the changes each time. That would be a slight improvement but still not quite good enough. I tried to be very structured to make sure I followed through and didn't lose interest halfway through but it would have been better to break it into more than 20 parts. I've used Ninject to make it easier to wire things together and something like that would work well for this kind of project; I thought about it but didn't want to add another post about how to use a dependency injection framework.

In summary
  • Use something to counter the limits to show refactoring on a series of blog posts.
  • Don't limit yourself to a fixed number of posts.
  • The Screen interface was the best thing I did: keep doing that.
  • The AsciiPanel was useful but it needs work to avoid flickering and to support animations.
  • Each post should have code, narration, references, and details about the author's thought process.
  • I liked getting comments from readers even more than I thought I would.
Any feedback from you, readers? What did you like? What did you not like? What could be done differently?

30 comments:

  1. Well, while I'm only on Tutorial 5 at the moment, I can say that you might want to revise some of the work, point out what you think can be done to improve it, or how to go about altering the structure of the whole thing to make it better. For example, the difficulties with a lack of global variables. As someone with fairly little experience programming, I found it difficult to follow your overall design decisions. Lots of things were passed into lots of things, and it gets confusing as to why we were doing it that way. Especially when doing so means were holding multiple instances of many things, which sounds like a terrible design decision. Of course, I may simply be misunderstanding something.

    Another interested ( perhaps a future tutorial series? ) topic would be writing efficient code, and how to optimize, and refactor it with later improvements. It's a very tricky subject, but it's definitely an area worth learning a bit about, but for which I've found little satisfying material.

    A few things I particularly liked was how you reference outside sources ( Wikipedia Articles ) for some topics of where to go with the ideas.

    I did run into a somewhat annoying problem, where I tried to create a terminal with a size above some maximum ( 50 for y ), and it took a while to figure out it was something with Asciipanel. Also, a more in-depth look at your Asciipanel would be worthwhile for us. I personally find dealing with JFrames, and the like to be the most confusing aspect of Java I've come across, and I could certainly use more insight into it.

    Anyways, thank you for your wonderful tutorials. They've been mostly easy to follow ( I did have to look at your code for refactoring for Creature.x ), and I especially enjoyed how we get results quickly. It's gratifying to be able to spend half an hour implementing the basics of a feature, and then to see it come into being.

    Oh, and perhaps some insight onto how to go about generalizing a bit of code? You know, for reuse. Sometimes I find it not so obvious how to go about doing that in a useful way.

    ReplyDelete
    Replies
    1. Passing an object into another doesn't mean you copy the object and have another instance of it, it means you're creating a reference to it.

      Delete
  2. @Seigeengine, Thanks for the feedback! I was trying to write something that was simple steps with quick results without global variables. It's a bit of a quick-and-dirty approach so you get a lot done in a short time (which is good), but it isn't the cleanest or most efficient way to do something (which can be bad). It's also easy to show adding new code but I'm not sure how to clearly show a refactor in a blog post. So what you said is spot on.

    I'm more of a C# than Java programmer so I also get mixed up with JFrames and other Java-framework stuff. I don't think I can be much help there.

    I have been playing with different forms of messaging and event sourcing lately so maybe I'll write something about that too.

    ReplyDelete
  3. I'm currently before the part in which you rewrite the world to work with 3D points. I'm taking my time with this, and I can say you're doing some things much better than I ever did.

    One thing that I found to work bad is... AsciiPanel. I'm working under Ubuntu Linux, and it just doesn't work well here. At first I saw ghosting of last ~15 images that were drawn, which I managed to fix. But the background is always blue for me, and all the coloring code doesn't work or works wrong, so I just stick to blue and white look for now.

    Sure I could try to fix it or write my own AsciiPanel, but it would be a waste of time when I'm just learning how did you structure the game and why.

    ReplyDelete
  4. @Areinu, sorry to hear about AsciiPanel; I only tried it on Windows 7 and Mac OSX but I thought it would work the same on Ubuntu. I'm not familiar with the details of Java graphics but if you have any ideas or fixes, you can let me know or start your own fork of https://github.com/trystan/asciipanel.

    ReplyDelete
    Replies
    1. @Trystan and @Areinu, I am currently having the same ghosting of last images that were drawn, and I just implemented part 2 of the tutorial. Running Fedora Linux with Oracle Java 7u2 here.

      The parts were the Strings of the previous screen were drawn are of a slightly darker blue and the text is in grey color. I wonder why terminal.clear(); does not do its job.

      The tutorial itself it top notch and I am learning a lot through it, thank you!

      Delete
    2. From what I've been able to find so far, the ghosting and odd colors are actual bugs in the Linux implementations of java. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6183251.

      Delete
    3. Hi,

      I was running into the same issues and followed the link you provided. There is a recommendation to use a ShortLookupTable as a workaround, which I did in the setColors method. I put the changes online as a fork on GitHub,
      https://github.com/toliwa/AsciiPanel/blob/master/src/asciiPanel/AsciiPanel.java

      Works now with a black background on my Linux machine without problems.

      Also, thanks a lot for these tutorials, Trystan!

      Delete
    4. I also switched to using shorts instead of bytes but wasn't able to test it yet - you beat me to it toltox! I've merged your fix and will update the AsciiPanel in the downloads and applets when I can. Thanks!

      Delete
    5. I'm using new AsciiPanel and it works wonders for me now. I got trough the tutorial up to magic. I'm changing a lot of stuff to my liking, and going with my own pacing.

      I found neat little bug in your code though, and it's pretty funny one :D If you get a bow and fire at yourself enough times to kill yourself you'll die... and get experience points for that. In fact enough to get level up most of the time. Leveling up will heal you up to above 0 HP before game over check has chance to see if you died. As a suicidal maniac that you are, you will be damned to wander dungeons in ghost like state for all eternity. It's so fun I might turn it into feature.

      Delete
    6. @Areinu, Ha! That's the best bug I've ever written.

      Delete
  5. I have no idea if your still reading this but this is super helpful i know several languages and java has been my biggest challenge(i guess i don't work the same as normal folk) me and friend entered the national stem video challenge(we're both in high8school) and figured entering a rogue-like while everyone else uses game-maker8 (the anti-Christ of coding) would be Boss, bringin' back the '80s and this really got the ball rolling we really didn't need any of the code (aside from the map algorithm :P me no good with crazy java) but more direction well done! I'm commenting without making some BS account but my nickname is Warmowed

    ReplyDelete
  6. Hey warmowed again i have 1 question how do you define a map and like build it without a random generator

    ReplyDelete
  7. @warmowed, The easiest way to load a simple premade map is to probably save it as a string (either as an external file or an inline string), then convert it to a char[][]. Then each char would represent a tile so you could convert that into Tile[][].

    Here's something to get started with. It's off the top of my head so it may not even compile, but it's a decent start.

    Tile[][] converToTileMap(String map){
    return convertToTileMap(convertToCharacterMap(map));
    }

    char[][] convertToCharacterMap(String map){
    String[] lines = map.split("\n");
    char[][] chars = new char[lines.length][lines[0].length];

    for (int i = 0; i < lines.length; i++)
    chars[i] = lines[i].toCharArray();

    return chars;
    }

    Tile[][] convertToTileMap(char[][] map){
    Tile[][] tiles = new TIle[map.length][map[0].length];
    for (int x = 0; x < map.length; x++)
    for (int y = 0; y < map[0].length; y++)
    tiles[x][y] = convertToTile(map[x][y]);

    return tiles;
    }

    Tile convertToTile(char character){
    switch (character){
    case '.': return Tile.FLOOR;
    case '#': return Tile.WALL;
    case '>': return Tile.STAIRS_DOWN;
    default: return Tile.WALL;
    }
    }

    ReplyDelete
  8. @warmowed, if you need to load and save more complicated data like creatures or complex tilesets, the easiest way is to just serialize it to and from xml. It may not be as easy to read and modify and I think you have to write your classes a certain way (public getters and setters for everything, it think), but it's easy to serialize things in java.

    ReplyDelete
  9. Is there a good way to prevent the game from flickering? Also, the game display seems to freeze up when I hold down a key. I'd love to make a roguelike, but I think these two problems would seem like a hassle and detract from the game as a whole. What should I do?

    ReplyDelete
    Replies
    1. There may be a way to prevent flickering and freezing up - it just depends on why it's doing that for you. It sounds like it's doing more work redrawing than your machine can comfortably handle. I haven't gone through and updated all the zip files but have you tried getting the latest AsciiPanel and using that? https://github.com/trystan/AsciiPanel/zipball/master

      Delete
    2. I actually haven't finished the tutorial yet; I'm using the java applet on your blog (http://trystans.blogspot.com/2011/08/nameless-roguelike-for-tutorial.html) to view the finished product before I've finished making it so I can (hopefully) get a better idea of what the result should be like. Is that one updated with the new AsciiPanel?

      Delete
    3. Yes, I just updated that reference a few minutes ago.

      Delete
    4. I refreshed my cache and loaded the applet again, but the flickering problem is still there, although it doesn't seem to be freezing up for me anymore.

      Delete
  10. Do you have any tips on optimizing speed? Made my worlds depth one but multiplied the size by 9, so it has a huge map. It generates pretty quickly, but my 526 monsters which are generated slow it down to an unplayable point during gameplay. Do you have any suggestions on speeding it up?

    ReplyDelete
  11. You should specify with every code block what class in belongs in. Sometimes it can be a little unclear.

    ReplyDelete
  12. I'm a bit late here, but thanks for this tutorial! I haven't read all of it yet, and I had to write my own terminal emulator (yours wasn't quite what I needed, and wasn't displaying things correctly all the time), but it's easy enough to convert your tutorial to my system. Thanks again!

    ReplyDelete
  13. Hey Trystan,

    thanks for your hard work and the nice library. The AsciiPanel thing is the best I have been using so far. It is nice and easy and I can use almost any font-graphics I want. Is it ok for you to fork and modify it? Thanks for any reply.

    ReplyDelete
  14. Hey Man, just wanted to say how great and useful a tutorial this is. You have my full respect, and in my case you've completely opened the door for video game development. Cheers

    ReplyDelete
  15. I'm at tutorial 4 and yeah, this is amazing. I started with Java about an year ago, and i thought that make a simple console game would be a good idea to improve my knowledge.
    So thank you, because i could never imagine how to do that.

    ReplyDelete
  16. Those are the perfect ground realities and surely would lead students towards all those respective guides and provisions of interest which are indeed considered to be so essential. tutor java

    ReplyDelete
  17. i couldn't think of a better place to post this, but i would love the ability to add my own colors to the asciipanel, i attempted to compile my own version from source with extra colors added, yet i couldn't get it to work.

    ReplyDelete
  18. these tutorials are amazing! I am about halfway through and I'm loving it. And learning a lot. If you could add some further steps on refactoring, or adding tests, that would be super cool.

    ReplyDelete