views:

206

answers:

7

I defined this simple method:

public static boolean isBorder(int x, int y) throws CollisionDetectionException {
        try {
            if ( (levelItems[x][y] instanceof StaticGameObject && levelItems[x][y].isVisible()) ||
                (levelItems[x-1][y] instanceof StaticGameObject && levelItems[x-1][y].isVisible()) ||
                (levelItems[x][y+1] instanceof StaticGameObject && levelItems[x][y+1].isVisible()) ||
                (levelItems[x][y-1] instanceof StaticGameObject && levelItems[x][y-1].isVisible()) ||
                (levelItems[x-1][y-1] instanceof StaticGameObject && levelItems[x-1][y-1].isVisible()) ||
                (levelItems[x-1][y+1] instanceof StaticGameObject &&levelItems[x-1][y+1].isVisible()) ||
                (levelItems[x+1][y] instanceof StaticGameObject && levelItems[x+1][y].isVisible()) ||
                (levelItems[x+1][y+1] instanceof StaticGameObject && levelItems[x+1][y+1].isVisible()) ||
                (levelItems[x+1][y-1] instanceof StaticGameObject && levelItems[x+1][y-1].isVisible()) ) {
                return true;
            } else {
                return false;
            } 
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new CollisionDetectionException("Collision couldn't be checked because checking position " + x + "/" + y + " caluclated values below (0/0)");
        }
    }

As you can see i have a 2dimensional array. Now i want to check a specific position ((x/y) -> the method arguments), if there are any visible StaticGameObject in the neighbouring fields of the 2dimensional array.

The levelItems array consists of so called GameObject. StaticGameObject is a a direct subclass of GameObject.

Any hints how to improve this method?

A: 

Don't make this any more complex. Simply be sure all subclasses of GameObject implement isVisible. That's all it takes.

If you need to also distinguish moveable vs. non-movable, then you need two methods:

  • isVisible -- visible or not

  • isMovable -- moveable or not

You never need instanceof.

[It's not clear from the question, but it appears that the class StaticGameObject actually means is a subclass of GameObject with isMovable() false.]

S.Lott
Yes they do, but there are other direct subclasses of GameObject that can be in the 2dimesnional array. And if there is another subclass than StaticGameObject I want to return false.
Roflcoptr
Then make the default value `False`. There aren't random "other subclasses" that will magically subvert this. Simply make it the default in the superclass.
S.Lott
@S.Lott: User Unknown specifically wants to find instances which are `StaticGameObject` _and_ are visible. The array may contain objects which are visible, but not the correct type.
Brian S
In which case, this `StaticGameObject` must implement `isVisible` correctly. You never need `isinstance`. All you need is to have your subclasses implement the methods correctly.
S.Lott
@S.Lott: Then, how would you solve the problem: e.g. MoveableGameObject that is also a subclass of GameObject and that also can be visible sometimes. How shall I tell if it is a MoveableGameObject or a StaticGameObject?
Roflcoptr
@UserUnknown: If moveable vs. non-movable is a feature of your objects, then add an `isMoveable` method.
S.Lott
+12  A: 

Add a method to GameObject

bool isBorderObject() { return false; }

Then override in StaticGameObject

bool isBorderObject() { return true; }

change the test to

(levelItems[x][y].isBorderObject() && levelItems[x][y].isVisible())

Also, the if could be this nested for

for (int i = x-1; i <= x+1; ++i) {
   for (int j = y-1; j <= y+1; ++j) {
       GameObject item = levelItems[i][j];
       if (item.isBorderObject() && item.isVisible()) 
           return true;
   }
}
return false;
Lou Franco
Thanks this looks great! So there is only one problem left, the long if statement;)
Roflcoptr
Yeah, I was working on that -- figured I do it in an edit
Lou Franco
@Lou Franco, I edited your code sample to make the if-statement and return more concise, hope you don't mind
matt b
You could write it as GameObject item = levelItems[x-1][y-1];return item.isBorderObject() its faster.
01
@matt b -- of course not -- I was thinking of doing that. But the code is wrong -- he only wants to return if it's true. Fixed it.
Lou Franco
+2  A: 

This should get rid of the extremely large if block you have:

for(int col = x-1; col <= x+1; col++)
{
    for(int row = y-1; row <= y+1; row++)
    {
        if(levelItems[row][col] instanceof StaticGameObject && levelItems[row][col].isVisible())
            return true;
    }
}

(This solution merely reduces the crazy if, not get rid of the instanceof, as you can see)

Of course, in both my example and yours, you should make sure to check for array bounds problems.

Brian S
isVisible is implemented in GameObject.
Roflcoptr
@User Unknown: Ah, sorry, I re-read your question and now I understand it better. Well, this solution reduces the extremely large `if`, at any rate. :)
Brian S
Hmm i tested this but it doesn't seems to work correctly. The borders aren't recognized as before with my method
Roflcoptr
That's because I'm an idiot and used `<` instead of `<=`. Fixing it now...
Brian S
+1  A: 

Think about "inversion of control".

As an example, how could introducing this method to the GameObject class:

public boolean
isBorder()
{
    return false;
}

and this override in the StaticGameObject class:

public boolean
isBorder()
{
    return self.isVisible();
}

simplify the code above?

Kaelin Colclasure
can't have static here and still get polymorphism
Lou Franco
Oops, thanks Lou… My Java is pretty rusty.
Kaelin Colclasure
+1  A: 

(Disclaimer: I've worked on Java games running on a lot of mobile devices)

People already showed how to simplify all these if statements. I'd add that defining your own CollisionDetectionException is probably completely overkill.

For a start, such low-level Java details don't exist at the OO level: exceptions, especially checked exceptions, are a Java idiosynchrasies that is really not necessary. Some very good and very impressive Java frameworks do mostly away with them, like Spring. Then a lot of very impressive and powerful apps, made of millions and millions of lines of code, run perfectly fine without ever using the concept of checked exception because they're, well, written in language in that don't have this kind of concept (once again: a "checked exception" doesn't exist at the OO level, hence any OOA/OOD to OOP without ever needing to use checked exception).

In any case: there's really no reason to turn an ArrayIndexOutOfBoundException into your own specific checked exception. This means you plan to use exception for flow control, which is a HUGE no-no.

Then, regarding your "isBorder" test... You probably don't need that. You think you do, but you really don't. Why do you want to know if it's a "border"? To detect a collision I guess.

You're using a static method to detect if it's a border, and static is the anti-thesis of OO. What is important is the messages you are passing around between objects. You may have all your objects responding to some isCollidingWith(...) message: objects that are "border" knows they are border, they'll know how to deal with a isCollidingWith(...) message.

Then you can go even further: instead of doing a isCollidingWith(...) you could have some resolveCollisionWith(...) method.

public class Wall implements MySuperAbstraction {

    int wallHitPoints = 42;

    boolean isWallStillUp = true;

    void resolveCollisionWith( SomeObject o ) {
        if ( o.isStrongEnoughToHarmWall ) {
           wallHitPoints--;
           isWallStillUp = wallHitPoints > 0;
        }
    }

}

This is just a quick piece of code and it doesn't deal with the fact that SomeObject needs probably to bounce when he hits the wall, etc. but the point stands: in OO objects knows how to communicate between themselves. They know how to deal with the various messages passed around.

If you want to do OO, then all I can tell you is that Java's static, instanceof and checked exceptions are definitely not the way the go. Basically, they're the anti-thesis of OO :) ]

NoozNooz42
+2  A: 

A revision to @Lou's solution which is to have one method and one loop.

for (int i = 0; i < 9; i++) 
   if (levelItems[x-1 + i/3][y-1 + i%3].isBorderObjectVisible())  
       return true; 
return false; 
Peter Lawrey
A: 

Another nice technique is to have a function that returns the set of adjacent cells. In this way you avoid (or anyway move) the double-loop. It's better separation of concerns; when you discover that your algorithm was forgetting about out-of-bounds conditions, there's exactly one place to fix it.

  public Set<GameObject> adjacentItems(int x, int y) {
    Set<GameObject> set = new HashSet<GameObject>();
    for (int i = -1; i < 2; ++i)
      for (int j = -1; j < 2; ++j)
        set.add(levelItems[x+i][y+j]);
    return set;
  }

  public boolean isBorder(int x, int y) {
    for (GameObject item : adjacentItems(x, y))
      if (item.isBorder() && item.isVisible())
        return true;
    return false;
  }
Carl Manaster