Design advice for a library I'm working on.

I’ve been working on this steering behavior library for a while, and recently I’ve made substantial progress however I’m starting to feel more and more uneasy about my design as I start making example games/simulations using the library.

I have these 3 different concepts: SpatialEntity, SteerSubject, & Target (my Vector class implements Target).

Issues arise when I want to use one in place of another, since they’re so similar (all have a position, 2 have a radius, one has velocity, direction & acceleration). For example I have a TargetClosest which given a SteerSubject will return the closest SpatialEntity. I have steering behaviors which take targets like this and determine whether to stay away from, steer to, steer around, hide from, etc. It returns a SpatialEntity (which SteerSubject implements BTW) because I didn’t want to say all spatial entities (which can be added to spatial databases) have a velocity, acceleration, etc…

My options as I currently see it:

  • Add a super interface to everything, it has: position, spatialGroups, spatialCollisionGroups, isStatic, isInert, direction, velocity, maximum velocity, acceleration, maximum acceleration. The defaults are 0 (or vectors at the origin), except position is required. This would mean 13 methods would be implemented, but in some cases (like Vector) it would only return one useful value. I think this is useful however, since maybe I want the unit to guard a Vector opposed to another unit, or the future position of a unit.
  • Add a lightweight component system, so that the super interface has getFloat|Vector where SpatialProperty is an enum and each implementation would instead have a switch statement on the enum which would be fairly fast.
  • Add an interface for each concept, like Spatial{isStatic,isInert,spatialGroups,spatialCollisionGroups}, HasPosition{position}, HasVelocity{velocity, maximum velocity}, and HasAcceleration{acceleration,maximum acceleration}. This will result in some very verbose code in the library, I will have generics like <T extends HasPosition & HasVelocity> and I don’t know whether that explicitness is a nice thing or not.

Another dilemma I’m working on is whether instead of having a radius, I can have a shape (which I have shapes implemented already: Segment, Plane, Bounds, Sphere). I liked radius initially because that keeps things super simple and fast. But I don’t know whether someone would maybe wan’t something else. Decisions…

If anyone could shed any light on this… I’ve had this exact same problem many times and I’ve always just picked one but I’ve always had bits of regret and I was hoping maybe someone can talk me into using one method or another so I don’t have to make these decisions.

My opinion, for what it’s worth:

It sounds like your problem is feature drift. You need to sit down and decide who this library is aimed at, a typical use case and therefore what features the library needs. When you’ve done that, have a good hard think of how you would like to be able to use the library and I find that the structure of the implementation tends to come quite easily. But only if I have thought it through thoroughly.

We could give you answers (well I would leave that to a better man anyway) but it’s your library and you are the one who has to be happy with it.

A good way to avoid feature drift is to build something concrete: a specific game rather than a library. Refactor it so you can use it to build the next couple of games more quickly… and you have a library. But maybe you have a client for the library, which would help keep it concrete.

As to your question, keep the interface generic, but opt for the simplest quickest implementationfor now so you can get the code working, probably the radius option you mention.

I’ve used another approach when I’m unsure about design: rather than focusing on the structure of the internals of your ‘engine’ consider how you would like it to be used by a hypothetical ‘user’. It’s easy to get bogged down in the details of class hierarchies, patterns, etc. when the important aspect is how your library delivers the features you want.

Write some test cases, demo applications or pseudo-code - whatever works for you - that exercise the functionality of your library, that may well highlight any iffy design decisions you have made, e.g. if you find that the demo code you write is ugly or overly complex then the underlying library code probably needs re-designing or cleaning up.

Re-write the test / demo code to be the way you (as the hypothetical user) would like it to work and you will hopefully derive a better structure, then focus on the details of the internals to deliver that API.

Might be worth a try.

  • stride