Showing posts with label entities. Show all posts
Showing posts with label entities. Show all posts

Thursday, 22 April 2010

Not Worth A Screenshot

It's not, but yer getting one anyway.


What's the significance of the arrows (ignore the inter-penetrating fungi)? That - right there - is some honest to goodness game logic! Green arrows are successful move actions, red ones are forced movement after failing an opposed test.

After months of being nothing but a rendering sandbox, there's actually a tiny bit of game in my game. Whoa.

Things to report so far:
  • The Ent system is really good for quickly working on game logic, almost as good as a decent dynamically-typed language. Adding additional properties can happen anywhere, which makes for very quick iteration, and handling things as Options makes writing systems to use them robustly equally easy. Working on a plan to allow more rigid use as the systems evolve, to help keep them under control as they grow.
  • Actually resolving the world state to something reasonable is really quite hard. I was hoping to avoid the addition of a 'prone' state to allow entities to share a position, but that's not strictly possible in the general case (N+1 entities occupying N grid positions) and annoyingly opaque even when it is possible. Compromises ahoy!
  • Must create new content. My re-use of the stupid "cave anemone" thing is bordering on the ridiculous now (the blue one is the player, by the way). Started sketching some monsters, and discovered that designing them such that they have a recognisable silhouette from directly above is... tricky. Maybe an orthographic three-quarters view would be better? Argh.
  • Did I mention Scala's for construct is awesome? I think I did. It bears mentioning again.

Monday, 19 April 2010

Wibble!

Mmm, lunchtime blogging.

First off, have a screenshot:


I've been playing with exponential shadow maps, and they're kinda fun. Not sure whether I'll use them over variance shadow maps, but it's tempting. I've also been messing with lit particles, although this has not been superbly successful. The image shows some alpha-tested jobbies that interact nicely with the deferred lighting, cast shadows and generally behave themselves, but the ultimate aim is to have nicely lit smoke effects and they do little to advance that goal.

I've been playing with rendering a lot of small fake-sphere particles to a render target and blurring it as a post process, but it's a bit too crude to work and smacks of megaparticles. I may go for a simple volume rendering approach.

I have moral objections to CPU-driven particles, which precludes the standard sort-and-render which makes such things easy. I'm also avoiding texture fetch in general (I got burned by ATI's render-to-vertex-buffer non-implementation in the past), so simulating particles in a position texture and doing the sorting GPU-side is not tempting either, although I may yet go back to that. In the meantime, the hunt for order-independent transparency continues!


do => for

I can't write much about this because I'm still feeling my way, but using a for expression to perform computation inside a monad is... weird, but quite nice. I'm not sure I prefer it to Haskell's do notation.

It's odd enough that I want to dump my exploratory fiddlings anyway, so here's a stub for the conflict-resolution step for two entities colliding after a movement phase. Note the awesome game logic for deciding the winner:
   private def collidePair( a: Ent, b: Ent ) : (Ent,Ent) = {
      val ab2 = for{
        aPos <- a.get[Int3]("position") if( a("solid",true) )
        bPos <- b.get[Int3]("position") if(aPos == bPos && b("solid",true) )
        aPosHis <- listToOption(a.history[Int3]("position").drop(1))
        bPosHis <- listToOption(b.history[Int3]("position").drop(1))
      } yield {
          val aWins = Math.random < 0.5
          if( aWins )
            (a,b.extend("position",bPosHis))
          else
            (a.extend("position",aPosHis),b)
        }
      ab2.getOrElse(a,b)
    }


Slightly odd exploratory code. Anyway, it crudely rips the current position from both entities, as well as their previous position, and yields an Option containing a tuple of new entities. If any of the extraction steps or predicates in the for chunk fails, it simply returns the original pair of entities.

Errors abound, of course. An entity doesn't always have a 'last' position, and if it does this in no way implies it's a reasonable place to go if it fails the conflict step. The listToOption thing is ugly as well. Bleh. But my original point stands: for is a weird and flexible beast in Scala, which I didn't appreciate before.

Monday, 22 March 2010

Type Frustrations III - Implicitly Manifest

Apologies for the title. It is bad.

I found this incredibly helpful, though, and set about stealing the ideas therein.

So, how's about this for a solution:

  case class Var[A]( name: String, tag: String )

  abstract class Env {
    def apply[A]( v: Var[A] ) : A
    def get[A]( v: Var[A] ) : Option[A]
    def extend[A]( v: Var[A], x: A ) : Env = new Env {
      def apply[B]( w: Var[B] ) : B = w match {
        case _ : v.type => x
        case _ => Env.this.apply(w)
      }
      def get[B]( w: Var[B] ) : Option[B] = w match {
        case _ : v.type => Some(x)
        case _ => Env.this.get(w)
      }
    }

    def apply[A]( n: String )( implicit m: scala.reflect.Manifest[A] ) : A = apply( Var[A](n, m.toString) )
    def get[A]( n: String )( implicit m: scala.reflect.Manifest[A] ) : Option[A] = get( Var[A](n, m.toString) )
    def extend[A]( n: String, x: A )( implicit m: scala.reflect.Manifest[A] ) : Env = extend[A]( Var[A](n, m.toString ), x )
  }

  object Empty extends Env {
    def apply[A]( v: Var[A] ) : A = error( "not found: "+v.name )
    def get[A]( v: Var[A] ) : Option[A] = None
  }

  //usage
  val e1 = Empty.extend("health", 95).extend("desc","wild fnord").extend("hates",List("humans","puppies"))
  val e1Health = e1[Int]("health")

  //this will be None due to type mismatch, as desired!
  val e1Hates = e1.get[List[Int]]("hates") 

The original has been extended by get, which is a slightly nicer way of handling likely-to-be-absent properties.

Manifests are strange beasts, but the above seems to work nicely. It behaves well when presented by parametrised types such as the List[String] in the example. They appear to be implicitly present without any imports, and although the code is slightly magical it's also a very clean solution to my problem.

Next task will be to make this serialise nicely, and a way to drop old vars. Currently every change to the entity will be recorded (a history method is trivial and could be useful), which is nice, but could eventually lead to very large entities. In particular, the player character will end up huge. Whilst I'm not exactly laser-like in my focus on performance here, 'potentially infinite' isn't a good size for a game object.

P.S. The very shiny backquote syntax in the match statements doesn't quite work in this case, as without the explicit use of the singleton type Scala doesn't believe that `v` can be of type Var[B].

Friday, 16 January 2009

Type Frustrations

Warning: this entry is largely a rant about my own ignorance.

I should have remembered this, but there's a big annoying thing that crops up whenever I try to get overly-generic in Scala.

Type erasure.

My understanding of this is that it's a pain in the arse. It has something to do with brutally discarding useful type information at runtime, such that List[Int] has the type List (or possibly List[Object]). Never mind that List is not even a type, it's a type constructor, some combination of the JVM and the writhing tentacles of an entire herd of shoggoths1 renders it a nigh-useless blob. Dark magic makes it work perfectly when all manipulations are done at compile time, but just try pattern matching or reflection with it...

What this means is that the following code will not compile:

//NB: Will not compile, type of [A] is unchecked due to erasure
def propTest( p: Property ) = p match {
case li:ListProperty[Int] => println( li.value.toString )
case lf:ListProperty[Float] => println( lf.value.mkString(":") )
case _ => println( "unknown property: "+p.toString )
}


Of course, this non-compilation is slightly less annoying than when you only try to pattern-match against a single instance of a parametrised type. In this case the compiler will emit a warning, but still compile to (apparently) working bytecode. If we dispense with the ListProperty[Float] case, the above will compile, li will match any ListProperty, and ClassCastExceptions or similar evil will almost certainly ensue.

Ignoring compilation warnings is (as we all know) incredibly dumb, and re-running compilation with the -unchecked flag will produce more helpful messages about what exactly is completely broken. It's still somewhat vexing that this compiles at all, when the chances of it doing anything useful are effectively zero.

I guess I can't complain overmuch, pattern matching against arbitrary type constructors appears to be something Haskell chokes on too, although with less smelly default behaviour and a different reason. I'm not sure how much of this is down to my downright shoddy understanding of type systems in both languages, so I'll be fiddling with this a lot more. In the interim, without some method for defining simple container properties, including type checking and pattern matching against them, the 'property bag' model appears dead in the water for my chosen implementation language.

As I said before: arse.

1What the hell is the plural of 'shoggoth'? For that matter, what's the collective noun for 'em?