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




