You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity C#类象棋游戏:子类间向上转型传事件信息遇阻

解决Unity C#类象棋游戏中抽象类子类间事件传递的问题

嘿,我完全懂你在做类象棋游戏时遇到的这个坎儿——用抽象类+向上转型实现子类间信息传递,尤其是事件这块,确实容易踩坑。先把你的代码补全并梳理清楚,再给你几个可行的解决方案:

先明确你的核心代码结构(补全你没写完的部分)

你定义的抽象类应该是类似这样的(我帮你补全了触发事件的方法):

public abstract class ChessPiece : MonoBehaviour
{
    // 定义攻击国王的委托
    public delegate void KingUnderAttack(
        int CurrentX, 
        int CurrentY, 
        int CurrentAttackerX, 
        int CurrentAttackerY, 
        System.Type attacker, 
        bool isKingWhite
    );
    
    // 声明事件
    public event KingUnderAttack kingUnderAttack;

    // 子类可调用的事件触发方法(必须protected封装,保证安全性)
    protected void RaiseKingUnderAttack(
        int kingX, 
        int kingY, 
        int attackerX, 
        int attackerY, 
        System.Type attackerType, 
        bool isWhiteKing
    )
    {
        // 空值判断避免空引用异常
        kingUnderAttack?.Invoke(kingX, kingY, attackerX, attackerY, attackerType, isWhiteKing);
    }
}

你可能遇到的问题&对应解决方案

问题1:子类无法正确触发抽象类的事件

很多时候是因为触发方法的访问权限不对,或者没做空值判断。解决方案就是上面代码里的protected void RaiseKingUnderAttack——子类可以直接调用这个方法来触发事件,比如你的兵子类:

public class Pawn : ChessPiece
{
    // 假设这是检测攻击国王的逻辑
    public void CheckKingAttackStatus()
    {
        // 模拟检测到攻击白方国王的情况
        int kingX = 4;
        int kingY = 0;
        int myX = (int)transform.position.x;
        int myY = (int)transform.position.y;
        
        // 调用抽象类的触发方法
        RaiseKingUnderAttack(kingX, kingY, myX, myY, typeof(Pawn), true);
    }
}

问题2:向上转型后事件无法订阅/触发

向上转型(比如ChessPiece piece = new Pawn();)后,抽象类的事件依然可以正常使用,因为事件是抽象类的成员,子类继承了这个成员。比如在棋盘管理类里订阅所有棋子的事件:

public class ChessBoard : MonoBehaviour
{
    void Start()
    {
        // 找到所有棋子(向上转型为ChessPiece)
        ChessPiece[] allPieces = FindObjectsOfType<ChessPiece>();
        
        // 给每个棋子订阅事件
        foreach (var piece in allPieces)
        {
            piece.kingUnderAttack += HandleKingUnderAttack;
        }
    }

    // 处理国王被攻击的逻辑
    private void HandleKingUnderAttack(
        int kingX, 
        int kingY, 
        int attackerX, 
        int attackerY, 
        System.Type attackerType, 
        bool isKingWhite
    )
    {
        Debug.Log($"{isKingWhite ? "白方" : "黑方"}国王位于({kingX},{kingY}),被{attackerType.Name}在({attackerX},{attackerY})攻击!");
        
        // 这里可以通知国王子类做出反应,比如高亮、判断是否被将军
        King targetKing = FindObjectsOfType<King>().First(k => k.IsWhite == isKingWhite);
        targetKing.OnUnderAttack(attackerX, attackerY);
    }
}

问题3:子类间直接通信耦合度太高

如果直接让国王子类去订阅其他棋子的事件,会导致代码耦合严重。更优雅的方式是用棋盘作为中间层——所有棋子的事件都通知棋盘,再由棋盘统一通知对应国王,这样子类之间完全解耦,符合面向对象的设计原则。

避坑提醒

  • 永远不要让子类直接操作事件(比如赋值kingUnderAttack = null),必须通过protected的触发方法来触发,保证事件的封装性
  • 在Unity里,不要在构造函数里订阅事件,因为Unity对象的构造函数执行时机早于Awake/Start,容易出现空引用
  • 记得用?.Invoke()做空值判断,避免没有订阅者时触发事件导致空引用异常

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

火山引擎 最新活动