You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何简化Java小行星游戏中的多重IF碰撞判断代码?

解决方案:告别instanceof地狱,优雅处理多对象碰撞

哥们,你这个问题太有代表性了——硬编码一堆instanceof分支的碰撞逻辑,别说拓展到100种对象,再加5种都能让代码变成维护噩梦。下面给你两个Java里实用的简化方案,从轻量到复杂场景都能覆盖:


方案1:碰撞处理器映射表(轻量首选)

核心思路是用哈希表存储「对象类型组合」与「对应碰撞处理逻辑」的映射,把分散的else if分支变成可动态注册的处理器,后续加新类型只需要加注册代码,完全不用碰碰撞循环的核心逻辑。

步骤1:定义碰撞处理器接口

先用函数式接口封装碰撞处理逻辑:

import java.util.Objects;
import java.util.function.BiConsumer;

// 用BiConsumer简化,也可以自定义更贴合业务的接口
@FunctionalInterface
interface CollisionHandler<T extends GameObject, U extends GameObject> extends BiConsumer<T, U> {}

步骤2:创建类型组合的唯一键

为了避免Ship-Meteor和Meteor-Ship被当成两个不同的键,我们自定义一个CollisionKey类,自动排序类型来保证键的唯一性:

class CollisionKey {
    private final Class<? extends GameObject> typeA;
    private final Class<? extends GameObject> typeB;

    public CollisionKey(Class<? extends GameObject> a, Class<? extends GameObject> b) {
        // 按类名字母顺序排序,确保A-B和B-A是同一个键
        if (a.getName().compareTo(b.getName()) <= 0) {
            this.typeA = a;
            this.typeB = b;
        } else {
            this.typeA = b;
            this.typeB = a;
        }
    }

    // 必须重写equals和hashCode,保证Map能正确识别相同的类型组合
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CollisionKey that = (CollisionKey) o;
        return Objects.equals(typeA, that.typeA) && Objects.equals(typeB, that.typeB);
    }

    @Override
    public int hashCode() {
        return Objects.hash(typeA, typeB);
    }
}

步骤3:注册碰撞处理器

在你的MyGame类里初始化映射表,把原来的ship_hit_meteor等方法注册进去:

import java.util.HashMap;
import java.util.Map;

public class MyGame {
    private final Map<CollisionKey, CollisionHandler<?, ?>> collisionHandlers = new HashMap<>();

    // 初始化方法里注册所有碰撞逻辑
    private void initCollisionHandlers() {
        // 注册飞船撞流星
        collisionHandlers.put(
            new CollisionKey(Ship.class, Meteor.class),
            (ship, meteor) -> ship_hit_meteor(ship, meteor)
        );
        // 注册子弹撞流星
        collisionHandlers.put(
            new CollisionKey(Bullet.class, Meteor.class),
            (bullet, meteor) -> bullet_hit_meteor(bullet, meteor)
        );
        // 后续加新碰撞(比如飞船捡硬币),只需要加这一行
        collisionHandlers.put(
            new CollisionKey(Ship.class, Coin.class),
            (ship, coin) -> ship_collect_coin(ship, coin)
        );
    }

    // 原来的碰撞处理方法保留
    private void ship_hit_meteor(Ship ship, Meteor meteor) { /* 逻辑不变 */ }
    private void bullet_hit_meteor(Bullet bullet, Meteor meteor) { /* 逻辑不变 */ }
    private void ship_collect_coin(Ship ship, Coin coin) { /* 新增逻辑 */ }
}

步骤4:简化碰撞循环

原来的一堆else if直接替换成查表执行:

// 碰撞循环内
GameObject a = objArray[i];
GameObject b = objArray[j];

CollisionKey key = new CollisionKey(a.getClass(), b.getClass());
CollisionHandler<?, ?> handler = collisionHandlers.get(key);
if (handler != null) {
    // 因为注册时保证了类型匹配,所以强制转换是安全的
    @SuppressWarnings("unchecked")
    CollisionHandler<GameObject, GameObject> castHandler = 
        (CollisionHandler<GameObject, GameObject>) handler;
    castHandler.accept(a, b);
}
// 循环结束

方案2:访问者模式(复杂场景首选)

如果你的游戏后续会有大量复杂的对象交互逻辑(比如流星撞硬币会分裂、子弹打不同流星有不同效果),访问者模式能把每个对象的碰撞逻辑集中管理,代码结构更清晰。

步骤1:定义碰撞访问者接口

interface CollisionVisitor {
    void visitShip(Ship ship);
    void visitMeteor(Meteor meteor);
    void visitBullet(Bullet bullet);
    void visitCoin(Coin coin);
    // 新增对象类型时,在这里加对应的visit方法
}

步骤2:让GameObject子类支持访问者

在父类中定义accept方法,子类实现它:

abstract class GameObject {
    // ...你的原有属性和方法
    public abstract void accept(CollisionVisitor visitor);
}

class Ship extends GameObject {
    @Override
    public void accept(CollisionVisitor visitor) {
        visitor.visitShip(this);
    }
}

class Meteor extends GameObject {
    @Override
    public void accept(CollisionVisitor visitor) {
        visitor.visitMeteor(this);
    }
}
// 其他子类(Bullet、Coin)同理实现accept方法

步骤3:实现具体的碰撞处理访问者

在MyGame里创建内部类,集中处理所有碰撞逻辑:

class CollisionHandlerVisitor implements CollisionVisitor {
    private GameObject otherObject;

    // 每次处理碰撞前,设置另一个碰撞对象
    public void setOtherObject(GameObject other) {
        this.otherObject = other;
    }

    @Override
    public void visitShip(Ship ship) {
        if (otherObject instanceof Meteor) {
            ship_hit_meteor(ship, (Meteor) otherObject);
        } else if (otherObject instanceof Coin) {
            ship_collect_coin(ship, (Coin) otherObject);
        }
        // 其他和Ship相关的碰撞逻辑都放这里
    }

    @Override
    public void visitMeteor(Meteor meteor) {
        if (otherObject instanceof Bullet) {
            bullet_hit_meteor((Bullet) otherObject, meteor);
        } else if (otherObject instanceof Ship) {
            ship_hit_meteor((Ship) otherObject, meteor);
        }
        // 其他和Meteor相关的碰撞逻辑都放这里
    }

    // 实现其他visit方法...
}

步骤4:简化碰撞循环

CollisionHandlerVisitor visitor = new CollisionHandlerVisitor();

// 碰撞循环内
GameObject a = objArray[i];
GameObject b = objArray[j];

// 处理a和b的碰撞
visitor.setOtherObject(b);
a.accept(visitor);
// 循环结束

方案选择建议

  • 如果只是想快速解决代码冗余、后续拓展简单,方案1的映射表更轻量,不需要修改现有GameObject子类的结构。
  • 如果游戏的碰撞逻辑复杂多变,每个对象和其他对象的交互都有独特规则,方案2的访问者模式能让代码结构更清晰,便于维护。

内容的提问来源于stack exchange,提问作者hamdirizal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:39:47