Man kann vermutlich einige Fälle auf den gleichen zurückführen. Also, vermutlich ist oft egal, in welcher Reihenfolge die Objekte stehen. Und Falls bei der Kollision etwas “kompliziertes” gemacht werden muss, kann man das ja recht leicht auslagern
class Unit {
void collideWith(Collectable other) {
Utilities.handleUnitCollectable(this, other);
}
}
class Collectable {
void collideWith(Unit other) {
Utilities.handleUnitCollectable(other, this);
}
}
Aber gerade, wenn es um “viele” Objektarten geht, könnte man sich irgendwelche Verallgemeinerungen vorstellen. Man wird nicht drumrumkommen, die ganze Matrix abzudecken, aber … wenn es jetzt wirklich 10 Klassen gibt, und jede davon 10 Methoden bräuchte, und morgen vielleicht noch 5 dazukommen, die aber nur bei der Kollision mit 2 der anderen relevant ist, könnte man auch grob sowas machen wie das hier: Auf Basis der Kombination der Klassen der beiden Objekte einen speziellen “Handler” raussuchen
package bytewelt.collide.generic;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
public class CollideExampleGeneric
{
public static void main(String[] args)
{
CollisionHandlers ch = new CollisionHandlers();
ch.register(Unit.class, Bullet.class, new CollisionHandlerUnitBullet());
ch.register(Unit.class, Unit.class, new CollisionHandlerUnitUnit());
MyGameObject a = new Unit();
MyGameObject b = new Bullet();
ch.handle(a, a);
ch.handle(a, b);
ch.handle(b, a);
ch.handle(b, b);
}
}
class CollisionHandlers
{
private final Map<Tuple2<?,?>, CollisionHandler<?,?>> map =
new LinkedHashMap<>();
<A, B> void register(Class<A> ca, Class<B> cb, CollisionHandler<A, B> ch)
{
map.put(Tuple2.of(ca, cb), ch);
}
void handle(Object a, Object b)
{
Tuple2<?, ?> key0 = Tuple2.of(a.getClass(), b.getClass());
Tuple2<?, ?> key1 = Tuple2.of(b.getClass(), a.getClass());
@SuppressWarnings("unchecked")
CollisionHandler<Object, Object> ch0 =
(CollisionHandler<Object, Object>) map.get(key0);
@SuppressWarnings("unchecked")
CollisionHandler<Object, Object> ch1 =
(CollisionHandler<Object, Object>) map.get(key1);
if (ch0 != null)
{
ch0.handle(a, b);
}
else if (ch1 != null)
{
ch1.handle(b, a);
}
else
{
System.out.println("No handler for "+key0);
}
}
}
interface CollisionHandler<A, B>
{
void handle(A a, B b);
}
class CollisionHandlerUnitUnit implements CollisionHandler<Unit, Unit>
{
@Override
public void handle(Unit a, Unit b)
{
System.out.println("Unit and Unit");
}
}
class CollisionHandlerUnitBullet implements CollisionHandler<Unit, Bullet>
{
@Override
public void handle(Unit a, Bullet b)
{
System.out.println("Unit and Bullet");
}
}
abstract class MyGameObject
{
}
class Unit extends MyGameObject
{
}
class Bullet extends MyGameObject
{
}
class Collectable extends MyGameObject
{
}
final class Tuple2<S, T>
{
/**
* Creates a new 2-tuple consisting of the given elements
*
* @param <S> The type of the first element
* @param <SS> A subtype of the first element type
* @param <T> The type of the second element
* @param <TT> A subtype of the second element type
* @param s The first element
* @param t The second element
* @return The new tuple
*/
public static <S, SS extends S, T, TT extends T> Tuple2<S, T> of(SS s, TT t)
{
return new Tuple2<S, T>(s, t);
}
/**
* The first element
*/
private final S first;
/**
* The second element
*/
private final T second;
/**
* Creates a new tuple consisting of the given element
*
* @param first The first element
* @param second The second element
*/
private Tuple2(S first, T second)
{
this.first = first;
this.second = second;
}
/**
* Returns the first element of this tuple
*
* @return The first element of this tuple
*/
public S getFirst()
{
return first;
}
/**
* Returns the second element of this tuple
*
* @return The second element of this tuple
*/
public T getSecond()
{
return second;
}
@Override
public String toString()
{
return "("+first+","+second+")";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + Objects.hashCode(first);
result = prime * result + Objects.hashCode(second);
return result;
}
@Override
public boolean equals(Object object)
{
if (this == object)
{
return true;
}
if (object == null)
{
return false;
}
if (getClass() != object.getClass())
{
return false;
}
Tuple2<?,?> other = (Tuple2<?,?>) object;
if (!Objects.equals(first, other.first))
{
return false;
}
if (!Objects.equals(second, other.second))
{
return false;
}
return true;
}
}
Unit and Unit
Unit and Bullet
Unit and Bullet
No handler for (class bytewelt.collide.generic.Bullet,class bytewelt.collide.generic.Bullet)
(Wobei man sich da aussuchen kann, ob die Reihenfolge eine Rolle spielen soll, oder nicht). Die Vor- und Nachteile muss man abwägen. Interessant daran könnte sein, dass man zur Laufzeit das Verhalten ändern könnte, und das es leichter erweiterbar ist. Aber das ist nur spontan hingeschrieben.