2017-03-18 8 views
0

Javaを学ぶために、私は古典的な "飛行機で宇宙" 2Dのゲームを作っています。プレイヤーオブジェクトに加えて、GameObjectクラスを拡張する独自のクラスを持つ多数の敵/障害物(スカベンジャー、ハンター、彗星、小惑星)が存在します。いくつかのスカベンジャー、コメットなどがあるので、これらはarraylistsに格納されています。しかしながら、各オブジェクトが互いに相互作用することができるので、例えば、各宇宙人が彗星のアライリスト、小惑星アレイリストなどのオブジェクトに従って相互作用するようにするために、多くのループおよび重複コードが存在する。ゲームのデザイン:オブジェクトarraylistsを整理する

私は私のゲーム更新機能で

public void update() { 

    ArrayList<Rock> rockList = rock.getRockList(); 
    ArrayList<Scavenger> scavengerList = scavenger.getScavengerList(); 
    ArrayList<Hunter> hunterList = hunter.getHunterList(); 
    .... 
    npc.update(player, scavengerList, hunterList, rockList); 
    ... 

}

と(ゲームオブジェクトクラスを拡張)私のNPCクラスで

public void update(Player player, ArrayList<Scavenger> scavengerList, ArrayList<Hunter> hunterList, ArrayList<Rock> rockList) { 

    for(int i = 0; i < scavengerList.size(); i++) {   
     scavengerList.get(i).update(player,scavengerList, ,hunterList rockList); 
    } 
    for(int i = 0; i < hunterList.size(); i++) {    
     hunterList.get(i).update(player,scavengerList, hunterList, rockList); 
    } 
... 
} 

をそして最後に、私は更新機能を持っていますスカベンジャークラス、私のハンタークラスなど

public class Hunter extends NPC{ 
... 
public void update(Player player,ArrayList<Scavenger> scavengerList, ArrayList<Hunter> hunterList, ArrayList<Rock> rockList) { 
"update all hunter objects according to the player, scavengers, rocks etc" 
} 

このアプローチはむしろ扱いにくいようであり、解析やループを繰り返す必要がある番号やarraylistsがより多くのクラスが作成されるにつれて手を出すようになっています。 これを行うより良い方法をお勧めする人はいますか? 明らかに、すべてのNPCオブジェクトを含む1つのリストを持ち、クラスの種類を追跡してそれに応じて更新するのが当然のことだと思います。 これはより良い方法ですか、誰でも正しい方向に向けることができますか?

+0

'World'インスタンスを渡す方が簡単でしょうか?基本的に 'for(updatableThing:updatableThings){updatableThing.update(world);}を実行できます。 } ' – plalx

+0

私は完全に理解していないかもしれませんが(まだ学んでいますが)、Worldインスタンスには、すべてのオブジェクトを含む、更新するリストが保持されているか、誤解していますか? – user2913053

答えて

2

はい、はるかに良い方法があります。

ゲーム内のオブジェクトの種類ごとに、表示する必要がある一連の動作/特性を調整します。これらの動作はインタフェースとして定義する必要があります。次に、ビヘイビア/特性を扱うコードは、実際のクラスについて何も知らなくてもインタフェースを使用できます。

public interface Moving { 
    void move(); 
    boolean hasCollided(Shape shape); 
    void handleCollision(Collision collision); 
} 

、このインタフェースを実装し移動させる任意のクラス:一部のオブジェクトは、それらの現在の速度に応じて各ターンを移動し、電位が他のオブジェクトと衝突することができれば

たとえば、インターフェイスがあるかもしれません。 Worldオブジェクトは、List<Moving> movingObjectsを持っているし、次に使用することもできます。その中に

movingObjects.forEach(Moving::move); 

update方法です。インターフェイスを実装するいくつかのクラスは、あなたが別のクラスにそのロジックを移動しなければならない自分自身を移動するためにも、同様の機構を使用している場合

List<Collision> collisions = getAllCollisions(movingObjects); 
for (Collision collision: collisions) { 
    for (Moving element: collision.getCollidingObjects) { 
     element.handleCollision(collision); 
    } 
} 

:あなたのようなものがある可能性があります移動した後、衝突を処理するために

class Inertia implements Moving { 
    private Velocity velocity; 

    @Override 
    public void move(Shape shape) { 
     velocity.applyTo(shape); 
    } 

    @Override 
    public void applyForceFrom(Position position) { 
     velocity.accelerateAwayFrom(position); 
    } 
} 

その後、このクラスに彼らの移動行動を委任することができますあなたの世界のオブジェクト:

class Asteroid implements Moving { 
    private final Inertia inertia; 
    private Shape shape = new Circle(radius); 

    @Override 
    public void move() { 
     inertia.move(shape); 
    } 

    @Override 
    public boolean hasCollided(Shape other) { 
     return this.shape.intersects(other); 
    } 

    @Override 
    public void handleCollision(Collision collision) { 
     intertia.applyForceFrom(collision.getCentreOfMass()); 
    } 
} 

これは不要な間接的に見えるかもしれませんが、長期的には価値があることが経験から分かりました。詳細はDelegation Patternを参照してください。

オブジェクトごとに動きが異なる場合(重力によって影響を受けるもの、AIなどで制御されるもの)、またはクラスが複数のデリゲートをmove(重力や慣性など)またはクラスその動作が一意である場合は、moveを実装できます。このすべては、Worldがオブジェクトのクラスについて何かを知る必要がなくても発生する可能性があります。呼び出しはmoveです。

通常、スーパークラスからの動作を継承する目的で、extendsを使用しないでください。ハンターがGameObjectを拡張しているNPCを拡張する構造は、HunterにEnemyやAIControlledなどを拡張したいと思う時点まで便利です。ハードな経験では、OOコーダーには、これらのタイプの階層は最初は賢明かつエレガントに見えるが、より複雑な機能を追加すると管理が困難になる。

どのオブジェクトがどの動作インターフェースを実装しているかの詳細をすべて非表示にするには、WorldからVisitor Patternをご覧ください。これにより、世界中のオブジェクトは、ムーバーとして、次にAIAgentとして、次にユーザーがコントロールするオブジェクトなどとして、すべてのゲームオブジェクトを訪問することができます。訪問中に何をするか(まったく何もしない場合)。それはうまく適用されれば非常に強力ですが、それに慣れるのに少し時間がかかります。

最後に、Entity Component Systemと呼ばれるゲーム作成者が使用する非常に一般的なアーキテクチャパターンがあります。もしあなたがJavaを学んでいるのであれば、私はこれを当面無視したいと思いますが、あなたが真剣なゲーム開発者になれば、上で説明したアーキテクチャよりも改善されている可能性があります。

私は明らかに(例えばShapeCirclePositionVelocityCollisionなどの定義として)の例では、詳細の多くを残してきたが、それは一般的な考えです。これ以上のことがあります。オブジェクト指向設計の本やチュートリアルを探してみる価値はあります。

+0

非常に徹底的な答えをありがとうございます。これがどのようにして同じようなクラスのコードを繰り返すのを避けることができるかがわかりますしかし、オブジェクトの動きがお互いに依存する場合はどうでしょうか。岩石が衝突して方向を変えることができます。ハンターは岩を避けたり、プレイヤーを追いかけることができます。これは、移動関数がすべてのオブジェクトについて知っていて、それに応じて動きを計算することを必要とします。 move関数は、どのオブジェクトがそれを呼び出すかを知っていて、Movingリストをループしますか?これは、元の投稿で述べたように、基本的にすべての現在のリストが1つに結合されていますか? – user2913053

+0

私は答えの中で衝突の共通モデルを提供して、これをどのように処理するかを考えます。他の例でも同様のモデルが有効です。 – sprinter

+0

私の答えは今、ばかばかしくなってきています。それが役に立ちましたら、それを受け入れて、あなたに助けが必要なものがあれば別の質問をしてください。 – sprinter

関連する問題