>

저는 간단한 드래그 앤 슈팅 아케이드 농구 게임 인 새로운 프로토 타입을 만들고 있습니다. 이 프로젝트를 위해 저는 LibGDX 및 Box2d 물리 라이브러리와 협력하고 있습니다.

여기에서 게임을 할 수 있습니다 : 아케이드 농구를하십시오!

여기서는 게임의 커스텀 콜리 전 리스너입니다. 내가 해결할 수 없었던 한 가지 단점은 조명기 A 또는 조명기 B가 첫 번째인지 여부를 보장 할 수 없으며, 어느 것이 어떤 객체인지 보장 할 수있는 방법이 없다는 것입니다. 따라서 각 방법에서 두 가지 모두에 대해 테스트해야합니다. 올바른 것을 찾는 비품.

또 다른 문제는 클래스의 속성에 액세스하기 위해 빈번한 캐스팅을 수행해야하며 해당 클래스에 대한 참조를 저장하기 위해 box2d 본문의 사용자 데이터를 자주 사용한다는 것입니다. 코드를 읽고 이해하기 어렵지만주의를 기울이면 충돌이 발생하지 않습니다.

충돌 클래스는 다음과 같습니다.

public class BasketballListener implements ContactListener {
    private WormyBasketballWorld basketballWorld;
    public BasketballListener(WormyBasketballWorld basketballWorld) {
        this.basketballWorld = basketballWorld;
    }
    @Override
    public void preSolve(Contact contact, Manifold oldManifold) {   
    }
    @Override
    public void postSolve(Contact contact, ContactImpulse impulse) {
    }
    @Override
    public void endContact(Contact contact) {
    }
    @Override
    public void beginContact(Contact contact) {
        Fixture fixtureA = contact.getFixtureA();
        Fixture fixtureB = contact.getFixtureB();
        this.ballAndHoopCollision(fixtureA, fixtureB);
        this.ballAndTargetCollision(fixtureA, fixtureB);
        this.scoreDetection(fixtureA, fixtureB);
    }
    private void ballAndHoopCollision(Fixture fixtureA, Fixture fixtureB) {
        if (fixtureA.getBody().getFixtureList().get(0).getFilterData().categoryBits == CollisionFilter.HOOP) {
            if (fixtureB.getBody().getUserData() instanceof SimpleWeaponOrb) {
                //System.out.println("hoop collision with ball");
                this.basketballWorld.ballCollidedWithHoop(fixtureB.getBody().getAngle(), fixtureB.getBody().getLinearVelocity());
            }
        }
        if (fixtureB.getBody().getFixtureList().get(0).getFilterData().categoryBits == CollisionFilter.HOOP) {
            if (fixtureA.getBody().getUserData() instanceof SimpleWeaponOrb) {
                //System.out.println("hoop collision with ball");
                this.basketballWorld.ballCollidedWithHoop(fixtureA.getBody().getAngle(), fixtureA.getBody().getLinearVelocity());
            }
        }
    }
    private void ballAndTargetCollision(Fixture fixtureA, Fixture fixtureB) {
        if (fixtureA.getBody().getUserData() instanceof TargetPlatform) {
            ((TargetPlatform)fixtureA.getBody().getUserData()).markDestroyed();
        }
        if (fixtureB.getBody().getUserData() instanceof TargetPlatform) {
            ((TargetPlatform)fixtureB.getBody().getUserData()).markDestroyed();
        }
    }
    private void scoreDetection(Fixture fixtureA, Fixture fixtureB) {
        if (fixtureA.isSensor()) {
            if (fixtureB.getBody().getUserData() instanceof SimpleWeaponOrb) {
                if ((HoopSensor)fixtureA.getBody().getUserData() == HoopSensor.TOP_SENSOR) {
                    ((SimpleWeaponOrb)fixtureB.getBody().getUserData()).hasHitFirstTarget = true;
                    //System.out.println("orb has hit first sensor");
                } else if ((HoopSensor)fixtureA.getBody().getUserData() == HoopSensor.BOTTOM_SENSOR) {
                    //System.out.println("orb has hit second sensor");
                    if (((SimpleWeaponOrb)fixtureB.getBody().getUserData()).isValidForScore()) {
                        ((SimpleWeaponOrb)fixtureB.getBody().getUserData()).hasAlreadyScored = true;
                        //System.out.println("orb has scored");
                        this.basketballWorld.scored();
                    } else {
                        //invalidate balls that hit the second fixture before the first
                        ((SimpleWeaponOrb)fixtureB.getBody().getUserData()).hasAlreadyScored = true;
                    }
                }
            }
        }
        if (fixtureB.isSensor()) {
            if (fixtureA.getBody().getUserData() instanceof SimpleWeaponOrb) {
                if ((HoopSensor)fixtureB.getBody().getUserData() == HoopSensor.TOP_SENSOR) {
                    ((SimpleWeaponOrb)fixtureA.getBody().getUserData()).hasHitFirstTarget = true;
                    //System.out.println("orb has hit first sensor");
                } else if ((HoopSensor)fixtureB.getBody().getUserData() == HoopSensor.BOTTOM_SENSOR) {
                    //System.out.println("orb has hit second sensor");
                    if (((SimpleWeaponOrb)fixtureA.getBody().getUserData()).isValidForScore()) {
                        ((SimpleWeaponOrb)fixtureA.getBody().getUserData()).hasAlreadyScored = true;
                        //System.out.println("orb has scored");
                        this.basketballWorld.scored();
                    } else {
                        //invalidate balls that hit the second fixture before the first
                        ((SimpleWeaponOrb)fixtureA.getBody().getUserData()).hasAlreadyScored = true;
                    }
                }
            }
        }
    }
}

이 두 줄은 물리 세계가 내 커스텀 리스너를 사용하게하는 데 필요한 전부입니다 :

   BasketballListener listener = new BasketballListener(this);
    this.world.setContactListener(listener);

또한 물리학의 느낌에 대한 의견을 보내 주시면 감사하겠습니다.

  • 답변 # 1

    코드 복제 ball*Collision() 의 각  그리고 scoreDetection()  메소드에 중복 된 코드가 너무 많습니다. 그들 각각에서, 두 번째 외부 if  블록을 간단히 잘라낼 수 있습니다. 역 인수를 가진 메소드를 추가로 호출하면 동일한 효과가 나타납니다.

    this.ballAndHoopCollision(fixtureA, fixtureB);
    this.ballAndHoopCollision(fixtureB, fixtureA);
    ...
    private void ballAndHoopCollision(Fixture fixtureA, Fixture fixtureB) {
        if (fixtureA.getBody().getFixtureList().get(0).getFilterData().categoryBits == CollisionFilter.HOOP) {
            if (fixtureB.getBody().getUserData() instanceof SimpleWeaponOrb) {
               this.basketballWorld.ballCollidedWithHoop(fixtureB.getBody().getAngle(), fixtureB.getBody().getLinearVelocity());
            }
        }
    }
    
    

    Demeter of Demeter 이 코드는 데메테르 법칙에 풍부하게 침을 뱉었습니다. 다음과 같은 통화

    fixtureA.getBody().getFixtureList().get(0).getFilterData().categoryBits
    
    

    간단히 끔찍하며 코드 전체에서 사용되는 엔터티 정의에 심각한 디자인 문제가 있음을 나타냅니다.

    가장 나쁜 경우는 이러한 호출이 깊이 의존하는 객체의 상태를 수정하는 경우입니다 (예 : 와이즈 비즈  또는 markDestroyed() . 디버깅하기가 정말 어려울 수 있습니다.

    봉지

    hasHitFirstTarget categoryBits  그리고 hasAlreadyScored  직접 노출되어서는 안되며 getter/setter를 사용하여 액세스/수정해야합니다.

    없는 추상화

    hasHitFirstTarget 와 같은 많은 클래스 캐스트가 있습니다  또는 (SimpleWeaponOrb)   (HoopSensor) 로 확인 . instanceof 가 리턴 할 수있는 오브젝트의 계층 구조처럼 보입니다.  혼란 스럽거나 누락 된 추상화가 있습니다. 예제로 결론을 내릴 수 없기 때문에 이에 대해 더 말할 수 없습니다.

    getUserData()

  • 이전 c++ - (거의) 동적 크기의 잠금없는 작업 큐 (다중 읽기/쓰기)
  • 다음 c# - 추출 인터페이스