2011-11-20 75 views
0

我正在爲Android遊戲編寫Java遊戲引擎,我的引擎處理不同形狀的碰撞檢測。每個形狀都是它自己的類(正方形,圓形等),並從共同的抽象母體Collidable派生。我有一個物理管理員課程,主要檢查遊戲中是否有任何現有對象與另一個對象發生碰撞,然後在檢測到碰撞時執行相應的操作。碰撞檢查是在每個物理形狀子類內部實現的,如下面的代碼所示。Java繼承/ OOP - 調用父類只有句柄的特定子類的方法

public abstract class Collidable 
{ 
} 

public class Square extends Collidable 
{ 
    public boolean Collides(Square) {...} 
    public boolean Collides(Circle) {...} 
    public boolean Collides(Triangle) {...} 
} 

public class Circle extends Collidable 
{ 
    public boolean Collides(Square) {...} 
    public boolean Collides(Circle) {...} 
    public boolean Collides(Triangle) {...} 
} 

public class Triangle extends Collidable 
{ 
    public boolean Collides(Square) {...} 
    public boolean Collides(Circle) {...} 
    public boolean Collides(Triangle) {...} 
} 

public class PhysicsMgr 
{ 
    public boolean Collides(Collidable p1, Collidable p2) 
    { 
     return p1.Collides(p2); 
     // This obviously won't work because there is no Collides 
     // method in Collidable. I want it to somehow call the child's 
     // method and pass in p2 as its child type rather than as 
     // a parent. Or somehow do this: 
     return (p1.child()).Collides(p2.child()); 
      // I know that obviously nothing like this exists. 
    } 
} 

我知道「的instanceof」和真的不想檢查P1和P2與每一個碰撞形狀我的孩子類型。一定會有更好的辦法。我正在尋找解決當前問題的解決方法,或者最好是重新設計當前的碰撞檢測系統,以完全避免此問題。

謝謝!

+0

有一個碰撞檢測的標準方法檢查multimethod實現:http://www.codeproject.com/KB/recipes/mmcppfcs.aspx – AlexTheo

回答

3

你應該閱讀有關visitor pattern

+1

訪客模式絕對是一種方式。在Collidable中聲明抽象方法'Collides(Square)'等。同時聲明一個抽象方法'Collides(Collidable)',在每個具體子類中實現爲'collidable.collides(this)'。由於該代碼出現在每個具體的子類中,因此編譯器可以確定要調用哪個版本的「collides」。 –

+0

@TedHopp我同意,編輯答案。 – yurib

+0

@Ted:就是我在找的東西。完善!所以(對不起,這是我懶惰,沒有閱讀訪問者模式來回答這個問題),如果我在父項中聲明抽象方法,然後在具體子類中實現它們,使用訪問者模式的IS? –

0
public boolean Collides(Square) {...} 
public boolean Collides(Circle) {...} 
public boolean Collides(Triangle) {...} 

你將需要形狀的不同組合不同的實現(因爲沒有共同的算法,我認爲)。所以在某一時刻,將需要撥打instanceof。恐怕有一種抽象的方法或接口方法public boolean Collides(Collidable)在這裏不會有所幫助,而且你現在擁有的東西不能得到明顯的改進。這是一個關於面向對象限制的教科書案例,因爲這些碰撞檢測方法不能整齊地附加到任何形狀類上,它們居住在某個地方,就像你的物理管理器一樣。

+0

您可以通過使用@Ted Hopp描述的「double-dispatch」方法實際避免顯式的'instanceof'調用,但是它涉及相當多的重複或樣板代碼。 OTOH將爲您提供一種由編譯器進行的完整性檢查。無論哪種方式,這不是很好。 – Thilo

+1

您可以將需要非平凡實現的十字形方法的數量減半:一旦實現了,例如'Square.Collides(Circle)',那麼您可以定義'Circle.Collides(Square square){返回square.Collides(this); }'。 –

+0

是的,那是我正在談論的樣板代碼(另一個例子是'Collides(Collidable)'的重複實現,它只是爲了便於雙重調度而反彈)。還必須小心避免遞歸循環。但我同意你提出的方法是完全有效的。我不是說聚合物理管理器中的所有東西都更好。這兩種方法都有很多不足之處。但我沒有意識到第三種更令人滿意的方式。 – Thilo

1

對於初學者,我不會讓Collidable成爲抽象類。儘管可能有很好的理由,在我看來,這是一個「很」的情況,很多對象都可能會碰撞。

所以,他這樣說,這裏是我會建議:

// Assuming you're working in 2 dimensions 
public class Coordinates { 

    public Coordinates(float x, float y) { 
    // etc etc etc 
    } 
} 

public interface ICollidable { 

    // Using unusually long name to illustrate point, 
    // but feel free to rename. 
    public int getMaxDistanceFromCenterOfMass(Coordinates unitVector); 
    public Coordinates getCenterOfMass(); 
} 

,然後爲正方形,三角形,和Circle,我想實現的接口。

public class Square implements ICollidable { 

    @Override 
    public int getMaxDistanceFromCenterOfMass(Coordinates unitVector) { 
    // Must declare and initialize 
    return this.lengthOfSide; 
    } 

    @Override 
    public Coordinates getCenterOfMass() { 
    return this.centerOfMass; 
    } 
} 

public class Circle implements ICollidable { 

    @Override 
    public int getMaxDistanceFromCenterOfMass(Coordinates unitVector) { 
    // Must declare and initialize 
    return this.radius; 
    } 

    @Override 
    public Coordinates getCenterOfMass() { 
    return this.centerOfMass; 
    } 
} 

public class Triangle implements ICollidable { 

    @Override 
    public int getMaxDistanceFromCenterOfMass(Coordinates unitVector) { 
    // Must declare and initialize 
    return this.lengthOfSide; 
    } 

    @Override 
    public Coordinates getCenterOfMass() { 
    return this.centerOfMass; 
    } 
} 

然後,在你PhysicsMgr ...

public class PhysicsMgr { 

    public boolean Collides(ICollidable p1, ICollidable p2) { 
    Coordinates cm1 = p1.getCenterOfMass(); 
    Coordinates cm2 = p2.getCenterOfMass(); 

    int length = Math.sqrt(Math.pow(cm1.x - cm2.x, 2) + Math.pow(cm1.y - cm2.y, 2)) 

    // It is a misnomer to use coordinates as a unit vector, but if I defined a 
    // UnitVector class, it would be exactly the same with the exception of 
    // the class name for this situation. 
    Coordinates unitVector = new Coordinates((cm1.x - cm2.x)/length, (cm1.y - cm2.y)/length); 

    int collisionDistance1 = p1.getMaxDistanceFromCenterOfMass(unitVector); 
    int collisionDistance2 = p2.getMaxDistanceFromCenterOfMass(unitVector); 

    return (length - collisionDistance1 - collisionDistance2) <= 0; 
    } 
} 

一個主要的在這裏需要注意的是,使用從質量中心的maxDistance字面上只會給你的廣場,三角的近似值。確切地說,你將不得不聲明一些方向,theta,並計算沿着單位矢量從物體的質心到邊緣的距離(這將是棘手的,但確切的)。

另一件令人高興的事情是它可以讓你隨着引擎變得更加複雜,輕鬆添加其他可碰撞對象。這也使得所有對象都不必知道對方。

我是3年的物理TA,實際上是我第一次接觸編程。如果您對額外工作感興趣,請參考我們使用的書:http://matterandinteractions.org/對於程序員來說這很好,因爲它通過在python中使用編碼示例(特別是vpython http://vpython.org/)教授物理。所以這對於物理編程來說是一個很好的參考。

+0

逼近一切,因爲圓圈當然「修復」了計算中單獨情況的需要。我認爲,即使在混合中添加方向以達成「棘手」的公式,對於所有形狀也不一定準確,除非再次引入單獨的明確情況。 – Thilo

+0

介紹明確的案例不是要走到這裏的路。否則,支持任何新對象將需要將新代碼添加到物理引擎的已知Universe中的每個其他對象。我只是在簡單地調用實現,因爲大多數人不習慣用單位向量和theta方向來做物理;不是因爲它的神奇或鬆散定義。給定對象的單位向量和方向(以及對於像半圓等複雜對象的典型點數的cm距離),可以確定兩個對象發生碰撞的確切點。 – Dave

+0

嗨,首先感謝詳細的帖子!儘管如此,我應該在我的問題上更清楚一些。我的物體不是圓形,正方形和三角形,這只是它們的形狀。所以我有一個Entity類,它是真正的對象「玩家,壞人,障礙物,無論什麼」,都有一個「保護Collidable mPhysicsShape」的成員,它簡單地描述了該形狀的物理。由於我用2D做東西,而我的物體是一種基於物理學的遊戲,所以幾乎所有東西都被物理地表示爲某種原始形狀。儘管你的解決方案會導致所有事情都像一個圓圈一樣行事 –