package vooga.scroller.collision_manager; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import vooga.scroller.sprites.Sprite; /** * Currently, we are handling all collisions through CollisionManager. * CollisionManager uses reflection to figure out which visit() method needs to * be called for the Sprites that have just intersected. This is a much more elegant * way of handling collisions than the Visitor method we were previously using. * <br> * <br> * In order for the Collision Manager to know what VisitMethods.java to look into, CollisionManager * takes in a VisitMethods object. * <br> * <br> * Collisions can occur on two levels. The recommended level is when the two sprites * have an interface implementation. For example, rather than have a Mario/Koopa collision, * we would much prefer the more general IPlayer/IEnemy collision. This allows us to * condense the number of visit() methods needed. However, sometimes you need a specific * collision between a sprite that doesn't fit in any interface. In that case, you can * define the collision on a sprite level. For example, you could have a Mario/Star collision. * You would need to have a visit method that looks like "visit(Mario mario, Star star)" to handle * that kind of collision. * * * @author Jay Wang * */ public class CollisionManager { private VisitLibrary myVisitMethods; public CollisionManager (VisitLibrary visitMethods) { myVisitMethods = visitMethods; } /** * When two sprites intersect, handleCollision() is the method you want to call. This method takes * two sprites and IF you have defined a collision for these two sprites, handleCollision() will * pull the correct visit method and invoke it on your sprites. This is done through Java's reflection * feature. Specifically, what it is doing is matching method signatures. * * @param sprite1 * @param sprite2 */ public void handleCollision (Sprite sprite1, Sprite sprite2) { Class<? extends Sprite> clazz1 = sprite1.getClass(); Class<? extends Sprite> clazz2 = sprite2.getClass(); @SuppressWarnings("rawtypes") List<Class> classList = new ArrayList<Class>(); classList.add(clazz1); classList.add(clazz2); addInterfaces(clazz1, classList); addInterfaces(clazz2, classList); addSuperClasses(clazz1, classList); addSuperClasses(clazz2, classList); // classArray has all possible (need all 2 combos) Object[] sprites = { sprite1, sprite2 }; for(int i = 0; i < classList.size(); ++i){ for(int j =0; j < classList.size(); ++j){ @SuppressWarnings("rawtypes") Class[] classArray = {classList.get(i), classList.get(j)}; invokeVisit(classArray, sprites); } } } @SuppressWarnings("rawtypes") private void addSuperClasses(Class clazz, List<Class> classList){ Class superClass = clazz.getSuperclass(); if(superClass != null){ classList.add(superClass); addSuperClasses(superClass, classList); } } @SuppressWarnings("rawtypes") private void addInterfaces(Class clazz, List<Class> classList){ for(Class c: clazz.getInterfaces()){ classList.add(c); } if(clazz.getSuperclass() != null){ addInterfaces(clazz.getSuperclass(), classList); } } @SuppressWarnings("rawtypes") public Class[] getInterfaces(Class clazz) { if(clazz.getInterfaces().length != 0) return clazz.getInterfaces(); if(clazz.getSuperclass() == null) return null; return getInterfaces(clazz.getSuperclass()); } private void invokeVisit (@SuppressWarnings("rawtypes") Class[] classArray, Object[] sprites) { try { Method method = myVisitMethods.getVisitMethod(classArray); method.invoke(myVisitMethods, sprites); } catch (SecurityException e) { e.printStackTrace(); } /** * If there is No Such Method, that means the game designer does not want * anything to happen when this collision occurs. */ catch (NoSuchMethodException e) { return; } catch (IllegalArgumentException e) { //e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }