GDR Forum Index
Podcast Podcast
Dev Dev Logs
Search Search
RSS RSS
Register Register
Log in Log in
Reply to topic GDR Forum Index -> Game Developer's Refuge -> Development Log - Gaunt's Dungeon
View previous topic :: View next topic  
Author Message
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Sun Feb 05, 2012 6:47 pm    Post subject: Development Log - Gaunt's Dungeon Reply with quote

I was looking through some old files, and found a demo for a Gauntlet-style game I had started working on back in 2001 (at least, judging by the timestamps). Despite a ton of obvious gameplay flaws and occasional segmentation faults, I found the thing kinda super fun and ended up killing about an hour playing it. So the natural thought is - resurrect the thing!

Here are some screenshots from what I'll be calling "the prototype:"





The eyeball guys in the second picture shoot projectile weapons, which can break down those crumbled walls the same way the player's does. They're also generally dangerous and cool.

You can try the demo at http://www.rowf.net/gaunt/GauntProto.zip if you'd like, but no guarantees it will run anywhere. It happens to be playable under my wine configuration, so maybe it will still run elsewhere.

Some aspects of the gameplay that I really like:
- The enemies are really stupid, and the collision detection is rudimentary. This opens up some neat ways to kill a ton of enemies at once, whereas the hordes will normally just destroy you.
- The "colored keys" system is lifted straight from Doom, except you can collect as many keys as you want. This allows for some interesting map design decisions.
- Run-and-shoot is sufficient to kill pretty much any horde of enemies. But if you get caught from both sides, or run out of space to run, it can get intense.
- The collision detection system effectively turns spaces between diagonal walls into murder holes. You can use this, and so can your enemies.

I also remember having a later version of this where each character had special abilities - priest could heal, wizard could do some kind of splash damage-y thing, thief could pick locks, and fighter just had some kind of lame fire-in-all-directions thing. I don't know if I still have such a version, but I like the idea.

Anyway, since the game is so simple and easy to extend (new monster is basically just four sprites and some stats, maybe a projectile) I think this would be the perfect little side-project. It could also be a lot of fun with on-line cooperative multiplayer. [/img][/url]
View user's profile Send private message AIM Address
Sirocco
Moderator

Joined: 19 Aug 2005
Posts: 9459
Location: Not Finland
PostPosted: Mon Feb 06, 2012 4:23 am    Post subject: Reply with quote

Elf is about to die!

I'm always in the mood for a good Gauntlet clone.

Quote:

The collision detection system effectively turns spaces between diagonal walls into murder holes.


I think there was a clone called Demon Stalkers that allowed you to do that. I might be smoking crack or something. I remember some game doing that, and I abused the bejeezus out of it :)
_________________
NoOP / Reyn Time -- The $ is screwing everyone these days. (0xDB)
View user's profile Send private message Visit poster's website
Sirocco
Moderator

Joined: 19 Aug 2005
Posts: 9459
Location: Not Finland
PostPosted: Tue Feb 07, 2012 4:30 pm    Post subject: Reply with quote

I almost cleared the first level, but the eyeball thingies raped my ass :(
_________________
NoOP / Reyn Time -- The $ is screwing everyone these days. (0xDB)
View user's profile Send private message Visit poster's website
Sirocco
Moderator

Joined: 19 Aug 2005
Posts: 9459
Location: Not Finland
PostPosted: Sun Feb 12, 2012 6:38 pm    Post subject: Reply with quote

Sorry, I was poking around the program's assets. I thought it was cool that you're defining levels with bitmaps. With the way the game's laid out it really makes sense to go that route. I'm using that approach with a project I started recently. Even if you stick with 8bpp sources you can still have 256 unique 'things' on a map, which is plenty for most games.
_________________
NoOP / Reyn Time -- The $ is screwing everyone these days. (0xDB)
View user's profile Send private message Visit poster's website
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Sun Feb 19, 2012 6:25 pm    Post subject: Reply with quote

Heh - back when I wrote the demo, I think using BMP for defining the levels was just so I didn't have to deal with writing a loader for a different file format :)

That said, it is really nice to be able to define a level using whatever graphics program you like (provided it can do indexed color, in this case). Saves writing a map editor, at least. Downside is that this means performing mental conversions, but that's actually not too bad. "Yellow pixel = yellow door" is not that hard to remember, really.
View user's profile Send private message AIM Address
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Mon May 28, 2012 10:05 am    Post subject: Reply with quote

Have not made much progress (basically just a flying turkey leg to convince myself the basic engine worked). I got excited by the entity/component systems thread, so I'm playing around with implementing something along those lines.

Here's an example with the sort of code I come up with when using this approach. I find it very exciting not to have to use curly braces. I like the idea that complex systems can be decomposed into such small nuggets of logic.

Code:
public class Solid implements Collide {

    public void invoke(Entity e, Entity other) {
        for (Boundary b1 : e.get(Boundary.class))
            for (Boundary b2 : other.get(Boundary.class))
                if (b1.overlaps(b2))
                    for (Impact impact : other.get(Impact.class))
                        impact.invoke(other, e);
    }

}


Code:
public class Collider implements Module {

    public void run(World w) {
        for (Entity e : w.getEntities())
            for (Collide c : e.get(Collide.class))
                for (Entity other : w.getEntities())
                    c.invoke(e, other);
    }

}


Here, Solid is a Component that an Entity might have. It examines other Components that an Entity might have to determine if they overlap; if so, it informs the other Entity that it has run into something solid (invokes its Impact, if it has one). That's the little picture of collision detection.

In the big picture, there is the Collider, which is a Module that the Engine might run. It simply finds all entities with Collide behaviors and invokes them upon all possible candidates. (Note that this is not a very efficient approach. Some time later I will put together a modified World that reduces the search space to some reasonable locality.)

I'm using github to store my code (mainly so I can easily work across machines), but feel free to poke through if anyone is curious - https://github.com/Skymouse/Gaunt. One note about class names: Long class names (SolidCollisionBehaviorComponent) are a pet peeve of mine, so I have chosen to rebel and only use one-word class names for all public class names in the project. I'm not saying it's good or better than the alternative (I would be super-annoyed looking through API docs for this, really), but I'm doing it and I'm enjoying it.
View user's profile Send private message AIM Address
Gil
Developer

Joined: 14 Nov 2005
Posts: 2341
Location: Belgium
PostPosted: Tue May 29, 2012 6:36 pm    Post subject: Reply with quote

See, this is why LINQ is so powerful in C#. Sample 1 would look like this in LINQ:

Code:

public class Solid implements Collide {

    public void invoke(Entity e, Entity other) {
        return
            from b1 in e.get(Boundary.class)
            from b2 in other.get(Boundary.class)
            from impact in other.get(Impact.class)
            return impact.invoke(other, e);
    }

}

_________________
PoV: I had to wear pants today. Fo shame!
View user's profile Send private message Visit poster's website
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Wed May 30, 2012 8:52 pm    Post subject: Reply with quote

I agree, that's much nicer than misusing Java's enumeration syntax. (Well, not quite "misusing" if an Entity has multiple Collide behaviors, or something, but that is hardly the norm)

Still, what I find myself wanting, as a programmer, is "Solid implements Collide as other.impact(self) if self.boundary.overlaps(other.boundary)" and be done with it - that's all the real logic. In other words, I want my if-nulls (or for-eaches / maps) to be implicit, at least in this specific case. And, for that matter, I want my getters to be implicit, too. (Ruby does a not-bad job at this, but I also like type-safety.)

Very often I find myself in a situation where some member is really only distinguished by its class identity, and I have to add a lot of garbage syntax:

Code:
class Bob {
  private:
    Shirt shirt;
  public:
    Shirt getShirt() { return shirt; }
    void setShirt(Shirt shirt) { this.shirt = shirt; }
}


If I were speaking, I could just say "Bob's shirt" and everyone would know exactly what I was talking about. So, I mean, it can be done.

Rant having been said, I don't really have a good solution for that. At minimum it seems like you'd need to add a lot of language rules (that might result in very unexpected behavior if forgotten), and probably would create more work recovering specificity when needed.
View user's profile Send private message AIM Address
Gil
Developer

Joined: 14 Nov 2005
Posts: 2341
Location: Belgium
PostPosted: Thu May 31, 2012 3:16 pm    Post subject: Reply with quote

All of that sounds possible with some decent architecture skills. Implicit getters and setters are again available in C# and LINQ lets your if-nulls be implicit if you implement a Maybe monad. For-eaches/maps are, as shown, already implicit in LINQ as the List monad :)

Sorry for all the C# promotion, I do not expect you to drop Java, it's just funny that you want stuff as a programmer, which is already available by using a more powerful language (not saying one is better than the other, Java's purity appeals to me too).

The only problem I see with C# is that the open-source (and open platform) implementation (Mono) lags a bit behind from time to time and is a bit harder to work with.
_________________
PoV: I had to wear pants today. Fo shame!
View user's profile Send private message Visit poster's website
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Thu May 31, 2012 7:45 pm    Post subject: Reply with quote

I find myself using Java out of a "least of all evils" more than a "best of all languages" mindset, so you can say whatever you like about it. I've avoided C# out of general Microsoft stigma but will have to check it out. Also never got too deep into FP (it appeals to my math geek side, but I feel like it's actually the wrong direction for application/game programming - i.e., you shouldn't need to worry about category theory en route to cloning friggin' Gauntlet) but it is good to see more instances of its really useful concepts worked into production languages.


How concise does LINQ syntax get? From the examples I've seen, the semantics are a lot clearer/cleaner than the equivalent Java/C++, but still rather more verbose than I'd like.
View user's profile Send private message AIM Address
Gil
Developer

Joined: 14 Nov 2005
Posts: 2341
Location: Belgium
PostPosted: Fri Jun 01, 2012 8:40 am    Post subject: Reply with quote

Skywise wrote:
I find myself using Java out of a "least of all evils" more than a "best of all languages" mindset, so you can say whatever you like about it.
Oh, I'm not saying anything bad of it. Java is made to be a very pure OO language and it works perfectly when trying to do OO. It's not a very powerful language, but if you want ULTIMATE POWER, you need to go to something like Python, which means losing your statically typed stuff, etc. It's all about the right language for the right job and I'm not sure if I should even be advising C# in game land. So yeah, I'm not advocating C#, just showing that it's nifty for doing entity style programming.

Skywise wrote:
I feel like it's actually the wrong direction for application/game programming - i.e., you shouldn't need to worry about category theory en route to cloning friggin' Gauntlet
The biggest misconception about FP is that you have to worry about category theory, you don't even need to know it. But yeah, it's a steep learning curve if you have been programming for years and have never felt like you needed it. I got into it by teaching it to myself and then noticing I was starting to want it. Slowly you'll notice FP creeping into your code and then you can start thinking about expanding on it. In the beginning it can be handy for eliminating if-nulls for example, we all hate those.

Skywise wrote:
How concise does LINQ syntax get? From the examples I've seen, the semantics are a lot clearer/cleaner than the equivalent Java/C++, but still rather more verbose than I'd like.
Well, you've got two ways of writing LINQ. You can use the special SQL-like query language and get some more power or you can get more concise by writing your own chainable methods on top of LINQ's chainable methods. I could give you some more examples if you want or I could maybe rewrite part of your code-base as an example project. That might actually be fun to work on.

So yeah, TL;DR: I just kinda hijacked your thread to show some of the wonders of C#, but I'm by no means trying to get people to switch over. If this discussion doesn't fit here, I'll just get out of your way :)
_________________
PoV: I had to wear pants today. Fo shame!
View user's profile Send private message Visit poster's website
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Fri Jun 01, 2012 8:02 pm    Post subject: Reply with quote

I've been actively re-evaluating languages lately, so I definitely don't mind the hi-jack - it's useful info. I didn't mean for my "category theory in Gauntlet" comment to be dismissive (but I can see that it kinda reads that way); I was just expressing some general feelings of "we're doing it wrong" that have been nagging me, not trying to reject helpful advice.

The long version of my quandary:

The essential problem of programming is that people reason about abstractions ("I want to make a man throw axes at an orc") whereas computers basically just move data and do arithmetic ("mov bx, ax; add ax, 3") - we get programs only when we figure out how to express the former as the latter. Programming languages help by providing some intermediate level of abstraction that can be expressed as data movement and arithmetic in some deterministic way. Low-level languages generally provide just enough structure to understand the mathematics the way that humans do - you get to reason abstractly using the language, but only about arithmetic, and the rest of that conversion is left on the programmer's shoulders. A language can be considered "higher-level" it offers some further degree of abstraction. Object-oriented languages let you reason about objects and classes, which are pretty close to being things that people normally reason about, but that's generally just one layer - above or below that layer you're still stuck translating into mathematical reasoning. Having a more abstract form of mathematics available (set theory, functional programming, facts & rules) is useful if you're dealing with a mathematical problem, but I don't know that it gets you any closer in the general case. "The cardinality of the intersection of the set of points occupied by the axe and the set of points occupied by the orc is non-zero" is not really what I'm thinking when I think "the axe hits the orc" any moreso than the low-level bounding-box comparisons I might otherwise use. For those cases, the magnitude of the mental leap doesn't change much, just the direction.

Anyway, that's all I meant to gripe about with my comment. On the one hand, a lot of the fun of programming itself is in making that conversion - expressing your abstract person-think idea in some kind of appropriate mathy way. On the other hand, my result-oriented side - the one that wants the axe to hit the orc - feels like this is an awfully roundabout way of doing things, and so I wonder if there is a different direction that languages might take. (A well-done merge of FP and OOP sounds like a good step in that direction - Ruby does a pretty solid job with this, and it sounds like LINQ offers a lot as well)
View user's profile Send private message AIM Address
n29
Developer

Joined: 13 Sep 2005
Posts: 879

PostPosted: Sat Jun 02, 2012 10:08 am    Post subject: Reply with quote

ML was an impure functional language that had some nice features. Ocaml was a version of ML with an object system tacked on. F# is a .net functional languge that was supposedly inspired by Ocaml. Scala is a languge for the jvm that combines functional and oop paradigms. Scala creator also had a back round in ML.

Those are some options you may look into if you haven't already.
_________________
My Blog
View user's profile Send private message
Gil
Developer

Joined: 14 Nov 2005
Posts: 2341
Location: Belgium
PostPosted: Sun Jun 03, 2012 6:49 pm    Post subject: Reply with quote

That's a well thought-out post :)

I think you're still missing one point though: functional programming doesn't need to be heavy on the math. Functional programming just means abstraction towards behavior, rather than abstraction towards concepts (OO).

OO is perfect for modelling what an axe and an orc is, but pretty poor at explaining what hitting means. Functional programming is all about the verbs. Biggest point there is function composition. Say you have a charm of cleansing that heals you and removes status effects. With function composition, that's as easy to express as:

Code:

Charm.Behavior = healing + debuff;

OnTick()
{
  Charm.Behavior();
}

_________________
PoV: I had to wear pants today. Fo shame!
View user's profile Send private message Visit poster's website
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Tue Jun 12, 2012 8:37 pm    Post subject: Reply with quote

Definitely need to play around with Scala in the near future - I like Java as an environment but am getting kind of fed up with it as a language. So much boilerplate.

I've been reflecting a lot about your comment about nouns versus verbs - it's a good point. You can encapsulate a verb in an object (even in a very general sense), but the more natural way to represent a verb in OOP is as a method - Subject.verb(directObject). So yes, you can define Animal.eat(Food) and your dog can eat a pizza, but you can't say that work eats all your time. The power of metaphor (in human language and maybe in programming) is that you can work with only a sparse, flexible subset of an object's properties to infer the correct meaning of such a phrase. Eating as a specific combination of biological processes doesn't map, but its high-level concepts (a subject, for purposes of its own sustenance, takes in an object and makes it unavailable) do. That's one of the things that appeals about some of the concepts we've discussed - not only can you focus on defining the verb, but you can also introduce flexibility into the definition (the Maybe monad was a good example of this).

Another thing that struck me - and this is really more of an aside - is the way programming languages impose organization upon their definitions. One of the appeals of OOP, in principle, is the organization of logic: Procedures are defined in relation to the data they effect. That's works okay in practice, mainly because it's better than nothing, but it's awkward compared to the way we deal with natural language. Imagine wanting to know what "eat" in "dog eats a pizza" means, and having to find Dog (wherever that is), seeing nothing about eat, going up to Mammal (wherever that is), seeing nothing, going up to Animal... That's what we do in OOP, and although IDEs can help hide a lot of the tedium, it's still a little nonsensical (and whatever organization exists is contingent on the fact that we've limited the definition of "eat" to animals.) Before alphabetization, dictionaries did organize their information a bit like this (categorization) but ultimately a flat ordered list (assuming the user knew 26-nary search) turned out to be most efficient. I have no particular suggestion of how to bring this over to programming, but it does suggest to me that maybe deep class hierarchies should be reconsidered.

(It's also worth noting that there's a disconnect between the meaning of "function" and "verb". A function describes a thing that takes some inputs and gives some output; a verb describes a fairly inherently stateful transition involving one or more objects, with no particular output aside from the transition described. In language, functions are more like qualifiers or modifiers: The "plus" in "five plus five" is not a verb. "Add" is a verb, but you don't say "five adds five," but "five added to five," where some implicit party has carried out the state transition.)

Back on topic: Have been working a lot on Gaunt's Dungeon on the train to work, even in the absence of strong language support for metaphor. Can now display my old maps in the Java version (woo!) as well as spawn enemies (who currently just spin in place). Working on players & input now.

One thing that I like about the use of indexed-color images as maps is that it helps structure my decisions about what goes into the game: I'm both limited to 256 unique "things" to start with on a map, and motivated to make the best use of those 256 things. Since a flat (unordered) list of 256 things is hard to maintain or even plan, I divide it up into slices of 16 each. So that's 16 kinds of walls, 16 kinds of doors (which might be four colors of keys and four kinds of decoration), et cetera... I'm planning to allocate the last half of the spawn-space to enemies (giving me up to 128). Of course, this can also be broken down into categories (Undead, Critters, Demons, Humanoids...) which are convenient to size at 16. And since each monster has an index (0-15) in its own category space, this permits a sort of ranking, which, for instance, can be used to provide a baseline of common values - a Rank 0 creatures might typically have 10 Health and be worth 10 Experience, for instance. Having fully composable entities makes this sort of thing (essentially multiple inheritance) really easy to implement.

Another nice thing I've found about the whole approach is that my bugs tend to be fairly high-level - if my entities don't move it's because I forgot to give them a Speed component, not because I made a math error somewhere deep in some nest of physics. And it's almost never fatal, it's just a bummer: If your entities are composable you assume by default that maybe it doesn't fully support some behavior or another, and that's okay.[/list]
View user's profile Send private message AIM Address
n29
Developer

Joined: 13 Sep 2005
Posts: 879

PostPosted: Wed Jun 13, 2012 6:45 pm    Post subject: Reply with quote

Steve Yegge had a good post about Java and nouns:

http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

You might look into 'fluent interfaces' in Java as a way to get away from the tedium of instantiating object graphs. The thing that annoys me the most about class based languages is having to write a source file for every damn class. With dynamic languages with functional features like Python or Groovy, I use maps and lists and lists of maps etc to represent data, that way I don't have to write a bunch of class files. This is useful for exploratory coding where you don't know how things are going to work, you know things are going to change A LOT because you're not going to get it right the first few times anyway.

BUT for large programs, the code completion and navigation you get with statically typed languages can't be beat.

So it's toss up. After grousing about Java, how do you stand on that? It's actually really easy to integrate scripting langagues into your java code: Rhino, JRuby, Jython, Groovy, are a few.
_________________
My Blog
View user's profile Send private message
Gil
Developer

Joined: 14 Nov 2005
Posts: 2341
Location: Belgium
PostPosted: Fri Jun 15, 2012 7:02 am    Post subject: Reply with quote

To me, it'll be a question of which language can get to the following state first:

- I can use the language to program object-oriented
- I can use the language to program functionally
- I can use the language to query data efficiently

Why the distinction between functional and querying? Because in most programs, that's a completely different set of responsibilities. Most languages get 2 out of 3 right (alright, Java only gets 1 right, but it does it really, really good).

So yeah, the perfect language adapts to become a DSL, depending on what you need. You want your domain model to look object-oriented (because it is!), you want to express action dynamically and you want to be able to query data programmatically, as easily as you would on DB side.

I'm sure someone smarter than me can better define the different ways of working with a programming language, but you catch my drift.
_________________
PoV: I had to wear pants today. Fo shame!
View user's profile Send private message Visit poster's website
Ninkazu
Contributor

Joined: 21 Sep 2005
Posts: 482
Location: Seattle, WA
PostPosted: Fri Jun 15, 2012 3:31 pm    Post subject: Reply with quote

Racket is getting there. It's the leader of DSLs, it has software contracts, a class/object system, dyanamic typing, static typing and many useful libraries.
_________________
Get woke. Stay woke. ~@deray
View user's profile Send private message Send e-mail Visit poster's website
Gil
Developer

Joined: 14 Nov 2005
Posts: 2341
Location: Belgium
PostPosted: Fri Jun 15, 2012 3:55 pm    Post subject: Reply with quote

Ninkazu wrote:
Racket is getting there. It's the leader of DSLs, it has software contracts, a class/object system, dyanamic typing, static typing and many useful libraries.

That does sound crazy good. I forgot about language integrated software contracts, which is an important one too.

Time to move to another thread though, I feel bad enough as it is already about derailing this thread. More Gauntlet goodness I say :)
_________________
PoV: I had to wear pants today. Fo shame!
View user's profile Send private message Visit poster's website
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Sun Jun 17, 2012 8:56 am    Post subject: Reply with quote

Hooked up the major pieces together to get a walk-around demo. I'm doing something kind of weird to distinguish the player character from other entities (while remaining consistent):

    * First, one entity - the "dungeon" - is created, based on specifications from a map (just a png) and entity prototypes associated with its color indices
    * In the first invocation of its logic, the dungeon spawns a lot of other entities into the world (these then turn into visible creatures after one more logic cycle)
    * Meanwhile, an "initializer" cycle is running. It is given some initialization tasks, the most important of which is to find a "player" entity (just an entity with a Player component) and hook it up to user input (again, just by adding components to that entity)


On the one hand this feels very weird, on the other it is fairly simple to work with. Almost all of the game logic so far is short, simple loops to do one specific thing (which is trivially ignored by entities that don't care about that thing).

Anyway, a cool thing happened when I hooked in the collision system (and fixed a bug where entities were colliding with themselves and flying off the screen in protest). When I walked into another entity (just an animating character - no AI or damage or anything yet) to make sure I couldn't walk through it, I found that I could push it around! This wasn't a feature I'd meant to implement, it was just a natural side effect of two entities expelling one another from their boundaries. So now I'm thinking of adding some sokoban puzzles (more along the lines of "put the box on the trigger to open the door"), or at least the option for fighter-types to just shove their way through hordes of enemies if they like.

What I'm working on now is a good way to define entities (more properly, entity prototypes). I have a class called "Specification" which is really just a container for a list of components to add to an entity to make it some specific thing (ie, an orc). The nice thing is that specifications can be chained: If you want a zombie orc, then you just apply the specifications "zombie" and "orc" (or chain them together as a new, reusable specification.) This permits the movement of definitions into a declarative style:

Code:

Monster
Strategy: Chase
Size: 1

Undead (Monster)
Alignment: Evil
Vulnerability: Good
Fear: Light

Ghost (Undead)
Experience: 20
Health: 2
Strength: 4
Speed: 4


So, in the last block, a specification named "Ghost" is defined which first invokes the specification named "Undead", and then adds a few other components on to give greater specificity. (In practice, lines like "Experience: 20" really refer to parameterized specifications which, in that case, add a new Experience(20) component). This gives some of the benefits of inheritance, while still allowing rather granular composition. Most importantly to me, it permits a declarative syntax that looks like a stat-block. [/code]
View user's profile Send private message AIM Address
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Sun Jun 24, 2012 10:08 am    Post subject: Reply with quote

Parser for files of the sort listed previously is now basically working, although I still need to bridge the gap between prototype definitions (named) and in-map definitions (indexed) before it is really useful. That's a more minor detail.

Also did some profiling to figure out some bad memory usage - wanted to do that earlier than later in case something needed to change. In this case the core of the problem was how I was getting components from entities: Each time, I was creating a new collection (ArrayList) and populating it with some appropriate subset of the entity's component list (components of the requested class). I knew this wasn't efficient and that I'd need to change it eventually, but I was surprised by just how bad it was: About 150 megabytes of arrays were being used between GC cycles. The problem, of course, is that these "gets" are used everywhere (any time you want to do anything with an entity you look at its components) and are often nested, leaded to combinatorial dumbness.

For now, I'm maintaining a cache of these subsets. Components are requested by class, so I just store a map of previously-requested classes and their results. In principle this could contain a lot of redundant information (by maintaining separate list for subclasses, for instance) but in practice the classes that actually get requested are more-or-less peers on the class hierarchy. My bigger concern is maintaining this cache: Right now I'm clearing this whenever an entity's component list changes, but this can happen nearly every frame. Alternately, I could go about updating all the individual caches whenever an entity changes, but this is a big performance hit on mutation, which is less common than access, but still, every frame. Plus, if caches aren't cleared then they're made unavailable for garbage collection, in principle causing memory usage to grow indefinitely as more esoteric component requests are made. (Another option is to avoid those immutable entities that are switched out often, but I'd like to avoid imposing that restriction on the way other code is implemented.)

Another option I'm leaning toward is returning collections (or, more simply, Iterable objects) that point to the same original data, but just skip over anything that isn't of the selected class. (This also opens the door to passing in more interesting criteria than just class identity, but I don't have any specific uses for that in mind yet.) This is worse than caching in terms of performance, since you're traversing the whole set every time just to get at the subset, but memory usage is way smaller. This is where I'm leaning, but can't really go there until I have a good solution to the problem of modifications during iteration (not a problem currently since things looking at an entity are never hooked up to its real component list directly.)
View user's profile Send private message AIM Address
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Wed Jul 04, 2012 7:19 am    Post subject: Reply with quote

Parsing external definitions is basically done. Now when I want to change the way a creature / item / whatever works, I just edit some text file, not some source code. I don't know that this is really much more convenient, but it is more concise. Started work on an in-game map editor. It's felt unrealistic to do all my editing with indexed-color images (even though I still like that route for storage), plus multiple people here have expressed love for in-game map editors.

While there's still plenty of programming to do, it won't be long before I need more assets (sprites, maps, the above-mentioned text files) in order to make use of said programming. I've found that making sprites isn't quite as easy as I remembered it, but I'm getting back up to speed. Here are two attempts I made at a Minotaur sprite: The first was over the weekend, the second was this morning (after reading Tsugumo's classic tutorials, browsing pixelation, and - maybe most importantly - remembering in the shower this morning that "dude, things don't really have outlines.")



My standards are not particularly high for this game, but I want to at least remain consistent with the existing sprites (and not totally embarrass myself).
View user's profile Send private message AIM Address
Skywise
Member

Joined: 18 Dec 2005
Posts: 75

PostPosted: Sun Aug 12, 2012 7:22 pm    Post subject: Reply with quote

Have a decent little map editor complete, as well as bare minimum of AI. Closing in on having something meaningfully playable - still need to code logic for attacks, damage, and death, as well as stuff like items and doors.

Stating to be concerned about performance / memory use. The main choke point is my "Entity" class, which are just generic buckets of game logic ("Component" objects). Depending on what you fill the bucket with (rendering info, collision behavior, intelligence) you end up with a door, a monster, a player, or even a whole dang dungeon. An example of that is shown in the collision detection code above.

Anyway, where I'm choking is on maintaining this component data and returning its relevant subsets. For instance, if you call ent.get(Think.class) you get all the AI components associated with ent. I've tried three variant implementations so far, but each has draw-backs:

A) Maintain just the list of components; whenever a get(...) call is made, construct a new list containing just those components that are of whatever class is requested. This works fine. but memory use gets ridiculous: There are a LOT of deeply-nested get(...) calls, since it is used in basically all entity interactions. All those arrays are subject to garbage collection, but doing so much allocating/collecting gets costly.

B) Maintain just the list, and when get(...) is called, return Iterator that refers back to the original list, but skips over anything of the wrong class. This saves the system from allocating / deallocating arrays for every interaction, but is still pretty slow, and requires some care when removing components from an entity (since you are implicitly iterating through the component array during any interaction)

C) Do like in A, but also maintain a cache of previously returned values (as a map of classes to arrays). So if you get the same get(...) call repeatedly, you don't need to keep creating new arrays every time - just reuse the same subset found earlier. Whenever an entity is mutated (components added / removed), the caches just get cleared. This saves both time and short-term memory allocation, but is worse in terms of long-term memory use, since both the full set of components and a number of cached subsets are maintained side-by-side.

So far I'm going with (C) as the best answer (see https://github.com/Skymouse/Gaunt/blob/master/src/net/rowf/gaunt/world/Entity.java for implementation) but I'm still not thrilled with the results. Fortunately, I never really set efficiency as a goal for this project, but it leaves me wondering how I'd go about achieving the same thing if I were trying to fine-tune performance.
View user's profile Send private message AIM Address
Reply to topic GDR Forum Index -> Game Developer's Refuge -> Development Log - Gaunt's Dungeon

Use this link to get a Sign-On Bonus when you get started!

All trademarks and copyrights on this page are owned by their respective owners. All comments owned by their respective posters.
phpBB code © 2001, 2005 phpBB Group. Other message board code © Kevin Reems.