Pregunta

I'm currently using go to implement a Game server due to the awesome concurrency primitives. That has all been a breeze to implement and works very reliably. I come mostly from a Java background though and I am having trouble forgetting Java's type hierarchy. I need to have a collection of game objects which all share a similar data structure like position, velocity, etc., but they may all have different behavior within their update methods, or special fields to allow different functionality. Initially I had a struct containing all of the shared data, and each custom type would embed that struct. The trouble is using data structures for organization. I use a quadtree to keep clients notified about nearby object's state. This way I can pass the tree a pointer to the embedded struct of each game object and everything works.

The problem with this is I can't access the containing type once I query the quadtree. So for example,

type GameObject struct {
    Position Point
    Velocity Point
    ID int32
}

type Client struct {
    GameObject
    conn net.Conn
    // other fields
}

Now, when If I want to update nearby players about an object's state, I query the quadtree, but there is now way to determine if the GameObject is actually a Client, and there is no way to access it's connection to send packets.

From Java, I'm used to creating a base class and subclassing it for Client, etc. Then I can use instanceof to determine which ones need special treatment and cast them accordingly to access custom functionality. I think I could do something like the following:

type GameObject interface {
    Position() Point
    Velocity() Point
    ID() int32
}

type Client struct {
    pos Point
    vel Point
    id int32
    conn net.Conn
    // other fields
}

func (c *Client) Position() Point {
    return c.pos
}

func (c *Client) Velocity() Point {
    return c.vel
}

func (c *Client) ID() int32 {
    return c.id
}

With this method I can use a type assertion to isolate the clients, but this will lead to a lot of duplicate code for implementing other game objects. I assume there is a more idiomatic way to do something like this in go, and I appreciate any help I can get. If this is the go way, maybe someone can help me understand the reasoning behind this design.

¿Fue útil?

Solución

It's hard to answer the question without knowing more about the problem and your constraints, but here's three ideas.

First, perhaps you can store a composite type in the quad tree rather than just the GameObject pointer.

type QuadTreeEntry struct {
   GameOb *GameObject
   Entity interface{}
}

The "Entity" would be a *Client, and whatever else goes in your quadtree.

Second, you might consider if you need all of GameObject in the quadtree. Perhaps this would be better?

type QuadTreeEntry struct {
    Position Point
    Entity interface{}
}

This has the nice effect of removing an indirection to get at the Position in the QuadTree code, and it's harder to accidentally invalidate your QuadTree, since its not sharing Position data with the original entity.

Third, if efficiency isn't too important, you could do this:

type GameObjectGetter interface {
    GetGameObject() *GameObject
}

func (c *Client) GetGameObject() *GameObject {
    return &c.GameObject
}

So everything that goes in the quad tree would be a GameObjectGetter.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top