2015-03-13 125 views
2

我想要實現的碰撞庫使用它預定的形狀像接口可碰撞Golang接口投嵌入式結構

type Collidable interface{ 
    BoundingBox() (float64,float64,float64,float64) 
    FastCollisionCheck(c2 Collidable) bool 
    DoesCollide(c2 Collidable) bool 
    Collide(c2 Collidable) 
} 

type Circle struct{ 
X,Y,Radius float64 
} 

的想法是,我可以做

type Rock struct{ 
    collision.Circle 
    .... 
} 

,然後實現了該接口可碰撞,這樣我就可以把它傳遞給一個空間哈希表(期望一個可碰撞)。唯一需要做的就是重寫Collide()函數以滿足我的需求。

但是圓型的函數不能處理類型的岩石,即使是強硬的它也有嵌入的圓。

func (c1 *Circle) DoesCollide(i Collidable) bool{ 
    switch c2 := value.(type) { 
    case Circle: 
    //doesn't fire, as it is of type Rock (unknown in this package) 
    //Needed is something like 
    //if i_embeds_Circle then c2 := i_to_Circle 
    } 
} 

這可能嗎? 有沒有更好的方法?

+0

所以這個(https://play.golang.org/p/J5bvaEtLbM)會說明你的問題? – VonC 2015-03-13 18:27:28

+0

有點。評論岩石的Shape()函數(然後使用圓圈)。我的問題是不能到達選擇的默認部分,但是如果我將一塊石頭傳遞給DoesShape() – user2089648 2015-03-13 19:15:16

+0

那麼https://play.golang.org/p/rg_plLZMSM:'default :'部分是爲了說明'搖滾'沒有被檢測爲'圓形'成形者',即使'搖滾'具有'圓圈'作爲匿名字段。 – VonC 2015-03-13 19:21:31

回答

0

不知道如何脫離這個標記,但張貼它,以防萬一以任何方式幫助你。

http://play.golang.org/p/JYuIRqwHCm

+0

在你的例子中,圓圈知道岩石,在這種情況下,我也可以簡單地將界面轉換爲正確的類型。問題是,碰撞是在它自己的庫中,而沒有進一步瞭解稍後採用某些形狀的對象。 – user2089648 2015-03-14 08:47:44

0

如果要調用一個傳統圖書館是不可擴展的(而不能檢測Rock,只有Circle),似乎只有一個解決方案,通過一個Circle

參見this example,其中I使用非匿名字段「c」的Rock
(這意味着Rock必須實現接口,這是一個簡單的一行到Circle.Shape()

type Shaper interface { 
    Shape() 
} 

type Circle struct{} 

func (c *Circle) Shape() {} 

type Rock struct{ c *Circle } 

func (r *Rock) Shape() { r.Shape() } 

func DoesShape(s Shaper) { 
    fmt.Println("type:", reflect.TypeOf(s)) 
    switch st := s.(type) { 
    case *Circle: 
     fmt.Println("Shaper Circle %+v", st) 
    default: 
     fmt.Println("Shaper unknown") 
    } 
} 

func main() { 
    c := &Circle{} 
    DoesShape(c) 
    r := &Rock{c} 
    DoesShape(r.c) 
} 

然後輸出是:

type: *main.Circle 
Shaper Circle %+v &{} 
type: *main.Circle 
Shaper Circle %+v &{} 
+0

命名你的界面'Shaper'而不是'IShaper'可能會更習慣。只是在說'。 :) – 425nesp 2015-03-14 03:00:47

+0

在這種情況下,我不能重寫當兩個對象發生碰撞時被調用的碰撞函數,這需要爲嵌入形狀圓的對象定製。 – user2089648 2015-03-14 08:50:35

+0

@ user2089648 true:嵌入(通過匿名*字段*)在類型「B」內的類型「A」不會使「B」成爲「A」。這意味着需要採取不同的方法。如果'Rock'實現了'Collidable',至少你的庫會調用'Rock'的Collide()'版本? – VonC 2015-03-14 10:10:57

2

您嘗試使用面向對象的設計模式與繼承。這並不是Go如何做到這一點。除此之外,接口名稱以Java的'able'或等同的面向對象的語言結束。在Go中,慣例是用'er'結束接口名稱。

要回答你關於岩石的問題,我會建議所有可以碰撞到另一個東西的東西實現方法CollisonShape()返回collison.Shaper(例如Circle),你將用它來測試collison。這裏collison是你的軟件包的名稱。

// This interface is defined in the collison package. 
// Any object that may collide must implement that method. 
type Collider interface { 
    CollisonShape() Shaper 
} 

// This function defined in the collison package 
// test if two Collider collide into each other. 
func Collide(c1, c2 Collider) bool { 
    shape1, shape2 := c1.CollisonShape(), c2.CollisonShape() 
    ... 
} 

// This is how you would define an object that can collide. 
type Rock struct { 
    shape *collison.Circle 
    ... 
} 
// Implements the Collider interface. 
// The return type must be the same as in the interface. 
func (r *Rock) CollisonShape() collison.Shaper { 
    return r.shape 
} 

正如你看到的,我們使用的方法來訪問岩石的科裏森形狀。這讓我們寫

if collison.Collide(rock, spaceCraft) {...} 

這回答你的問題如何得到岩石的collison形狀。

如果您希望避免在Collide()函數中調用CollisonShape()方法,則必須直接傳遞collison.Shaper。

碰撞將在科裏森包被定義爲

func Collide(shape1, shape2 Shaper) bool {...} 

方法,然後你會寫

if collison.Collide(rock.shape, spacecraft.shape) {...} 

這種設計會稍微更有效,但付出的代價是少可讀的代碼,這是有經驗的Go程序員所憎惡的。

如果您希望Circle成爲嵌入式結構體,那麼您必須按以下方式對其進行定義。嵌入形狀可以節省Circle的分配時間和GC的一些工作。

type Rock struct { 
    shape collison.Circle 
    .... 
} 

if collison.Collide(&rock.shape, &spacecraft.shape) {...} 

如果你想使用匿名嵌入式結構,你再會寫

type Rock struct { 
    Circle 
    .... 
} 

if collison.Collide(&rock.Circle, &spacecraft.Rectangle) {...} 

正如你看到的,代碼變得越來越少可讀性,使用不太方便。形狀不再被抽象。使用匿名嵌入式結構應該僅限於極少數情況下才有意義。

通過使用最初建議的CollisonShape()方法,您可以輕鬆地將Rock結構更改爲此結構,而不會破壞任何代碼。

type Rock struct { 
    shape collison.Circle 
    ... 
} 


func (r *Rock) CollisonShape() collison.Shaper { 
    return &r.shape 
} 

這現在使形狀和嵌入式結構。使用一種方法來獲得形狀,將岩石的內部實現與形狀的訪問分離開來。您可以更改Rock的內部實現,而無需在其他地方更改代碼。

這是Go不支持繼承的原因之一。它在基類和派生類之間創建了非常強大的依賴關係和耦合。經驗表明,人們經常爲代碼演變的耦合感到後悔。對象組合是首選,Go推薦和支持。

如果效率是您的目標,那麼每個對撞機應該有一個位置發生變化,並且邊界框的寬度和高度不會改變。您可以使用這些值爲邊界框重疊測試保存一些操作。但這是另一回事。