views:

106

answers:

2

Hi all, I'm looking to create a scene-graph for my F# project somthing like:

root
->player_bob
-->torch
->enemy_1
-->extravagant_hat
-->enemies_cat_jess 
--->fleas
--->fur_ball
->loot

etc,etc.

Each item needs to hold a collection of game objects to represent it's children.

e.g enemy1's list contains a cat and a hat and the cats list contains fleas and a fur ball

So I plan to make them all inherit from a class that contains a collection that describes that objects children.

Now to my question: Should I down-cast child objects to GameObject's and store them in a list of the "GameObject" base class OR create a discriminating union e.g

type SceneObject = 
        |Character of Character //e.g player, enemy, cat
        |Item of Item //e.g hat, torch, furball

And store objects as a list of "SceneObjects" to avoid any problems/overheads with up-casting them, etc. As well as allowing me to describe special cases where the object isn't rendered and/or not used in collision detection e.g: sound emitters, trap triggers, etc.

The discriminated union + inheritance combo is my initial thought; though, as I'm new to FP, I thought it wise to ask the pro's for the best, functional, way of approaching this.

Thanks,

JD

+7  A: 

You can use the discriminated union recursively.

type SceneObject = 
    | Character of <characterData> * (SceneObject list) 
    | Item      of <itemData>      * (SceneObject list)

And use it like this

let root = [Character("Bob", [Item("Torch", []); ...]); ...]
Dario
Brilliant! This eliminates the need for the inheritance :¬)Thanks Dario, half my problem with learning FP is pulling my head out of OO land.
jdoig
+4  A: 

An alternative to what Dario suggest is to declare a separate type for actual objects in the game (such as items and characters) and wrap it in another object that adds a list of child objects. This would look like this:

type SceneObject =
  | Character of <characterData>
  | Item of <itemData>

type ObjectWithChildren = 
  { Object : SceneObject
    Children : ObjectWithChildren list }

Both of the options are correct functional design, but I would prefer this version, because it makes certain kinds of processing easier. For example, if you wanted to traverse all objects in the game tree, you can write something like:

let rec traverse tree = 
  // Do something with tree.Object
  for child in tree.Children do traverse child

In this version, you don't need to pattern match on SceneObject at all, so you don't need to include all the cases (such as Item and Character).

Tomas Petricek
Interesting. This is how I've modelled the Character class and its state machine.A character record to hold all the raw data (location, health, etc) (CharacterRecord).A mailbox processor as the state machine (CharacterStateMachine).And then wrapped the two up in the "Character" record that holds {state:CharacterStateMachine; data:CharacterRecord}.Then used members to make some user-friendly getters and setters...So this solution fits well with my current model. I'll have to give both of these a try,Many thanks.
jdoig