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

C# 2D平台游戏:用组合替代继承重构代码及敌人受击逻辑实现

2D平台游戏重构(继承转组合)+ 玩家踩敌头顶伤害逻辑实现

一、用组合重构继承代码

现有代码通过Character基类继承实现玩家与敌人的通用逻辑,但随着游戏规模扩大,继承会导致耦合过重、层级臃肿。用组合模式拆分不同职责为独立组件,每个组件只负责单一功能,后续扩展更灵活。

拆分后的组件实现

1. HealthComponent(生命值管理)

using UnityEngine;

public class HealthComponent : MonoBehaviour
{
    public int maxHealth;
    public int currentHealth;
    public HealthBar healthBar;

    private void Start()
    {
        currentHealth = maxHealth;
        healthBar?.SetMaxHealth(maxHealth);
    }

    public void TakeDamage(int damage)
    {
        currentHealth = Mathf.Max(currentHealth - damage, 0);
        healthBar?.SetHealth(currentHealth);
    }
}

2. DamageCooldownComponent(受伤冷却控制)

using UnityEngine;

public class DamageCooldownComponent : MonoBehaviour
{
    [SerializeField] private float damageInterval = 2f;
    private float currentDamageInterval;

    private void Update()
    {
        if (currentDamageInterval >= 0)
            currentDamageInterval -= Time.deltaTime;
    }

    public bool CanTakeDamage()
    {
        return currentDamageInterval < 0;
    }

    public void ResetCooldown()
    {
        currentDamageInterval = damageInterval;
    }
}

3. KnockbackComponent(被击击飞逻辑)

using UnityEngine;

public class KnockbackComponent : MonoBehaviour
{
    private Rigidbody2D rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    public void ApplyKnockback(float yVelocity)
    {
        rb.velocity = new Vector2(rb.velocity.x, yVelocity);
    }
}

重构后的玩家与敌人代码

玩家类(Player)

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour
{
    [SerializeField] private Transform groundCheck;
    [SerializeField] private LayerMask hazardLayer;
    [SerializeField] private float knockbackYVelocity = 15f;
    
    private Color[] colors = { Color.red, Color.white };
    private Coroutine damageFlash;
    private SpriteRenderer sr;
    
    private HealthComponent health;
    private DamageCooldownComponent damageCooldown;
    private KnockbackComponent knockback;

    private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
        health = GetComponent<HealthComponent>();
        damageCooldown = GetComponent<DamageCooldownComponent>();
        knockback = GetComponent<KnockbackComponent>();
    }

    private void Start()
    {
        health.maxHealth = 10;
        health.currentHealth = health.maxHealth;
        health.healthBar.SetMaxHealth(health.maxHealth);
    }

    private void Update()
    {
        if (damageCooldown.CanTakeDamage())
        {
            if (Physics2D.OverlapCircle(groundCheck.position, 0.5f, hazardLayer))
            {
                TakeDamage(1);
            }
        }

        if (Input.GetKeyDown(KeyCode.H))
        {
            TakeDamage(1);
        }
    }

    public void TakeDamage(int damage)
    {
        if (!damageCooldown.CanTakeDamage()) return;
        
        health.TakeDamage(damage);
        knockback.ApplyKnockback(knockbackYVelocity);
        damageCooldown.ResetCooldown();

        if (damageFlash != null)
            StopCoroutine(damageFlash);
        damageFlash = StartCoroutine(DamageFlashing(1f, 0.1f));
    }

    IEnumerator DamageFlashing(float duration, float interval)
    {
        int index = 0;
        var wait = new WaitForSeconds(interval);
        for (float elapsedTime = 0; elapsedTime < duration; elapsedTime += interval)
        {
            sr.color = colors[index % 2];
            index++;
            yield return wait;
        }
        sr.color = Color.white;
        damageFlash = null;
    }
}

敌人类(Enemy)

using UnityEngine;

public class Enemy : MonoBehaviour
{
    [SerializeField] private LayerMask playerLayer;
    [SerializeField] private float knockbackYVelocity = 15f;
    [SerializeField] private Transform topDamageCheck;
    [SerializeField] private float checkRadius = 0.3f;
    
    private HealthComponent health;
    private DamageCooldownComponent damageCooldown;
    private KnockbackComponent knockback;

    private void Awake()
    {
        health = GetComponent<HealthComponent>();
        damageCooldown = GetComponent<DamageCooldownComponent>();
        knockback = GetComponent<KnockbackComponent>();
    }

    private void Start()
    {
        health.maxHealth = 3;
        health.currentHealth = health.maxHealth;
        health.healthBar.SetMaxHealth(health.maxHealth);
    }

    private void Update()
    {
        if (damageCooldown.CanTakeDamage())
        {
            Collider2D playerCollider = Physics2D.OverlapCircle(topDamageCheck.position, checkRadius, playerLayer);
            if (playerCollider != null)
            {
                Rigidbody2D playerRb = playerCollider.GetComponent<Rigidbody2D>();
                if (playerRb != null && playerRb.velocity.y < 0)
                {
                    TakeDamage(1);
                    playerRb.velocity = new Vector2(playerRb.velocity.x, 10f);
                }
            }
        }
    }

    public void TakeDamage(int damage)
    {
        health.TakeDamage(damage);
        knockback.ApplyKnockback(knockbackYVelocity);
        damageCooldown.ResetCooldown();
        Debug.Log("Enemy taking damage");
    }
}

组合模式的优势

  • 每个组件职责单一,修改某一功能(比如调整击飞逻辑)只需要修改对应组件,不影响其他逻辑
  • 避免继承的“脆弱基类问题”,后续新增角色类型(比如NPC、Boss)可以自由组合需要的组件
  • 代码更易维护和测试,降低耦合度

二、玩家踩敌人头顶仅敌人受伤的实现

核心逻辑

实现该功能需要两个关键判断:

  1. 玩家是否接触到敌人的头顶区域
  2. 玩家是否处于下落状态(避免侧面接触误触发)

具体实现步骤

  1. 在Enemy对象下创建空物体topDamageCheck,放置在敌人Collider的顶部偏上位置(确保只有玩家踩上去才会触发)
  2. 用Physics2D.OverlapCircle检测该点是否接触到玩家,同时判断玩家的y轴速度是否小于0(下落)
  3. 满足条件时,调用Enemy的TakeDamage,同时给玩家一个向上的弹跳力(符合平台游戏手感)

替代方案:用Trigger碰撞体实现

如果不想用代码检测点,可以给敌人添加一个小型的Trigger碰撞体(比如CapsuleCollider2D),放在敌人头顶区域,然后修改Enemy的代码:

private void OnTriggerStay2D(Collider2D other)
{
    if (damageCooldown.CanTakeDamage() && other.CompareTag("Player"))
    {
        Rigidbody2D playerRb = other.GetComponent<Rigidbody2D>();
        if (playerRb != null && playerRb.velocity.y < 0)
        {
            TakeDamage(1);
            playerRb.velocity = new Vector2(playerRb.velocity.x, 10f);
            damageCooldown.ResetCooldown();
        }
    }
}

注意事项

  • 确保Player和Enemy的Layer设置正确,检测时使用对应的LayerMask
  • 调整checkRadius和弹跳速度数值,适配你的游戏手感
  • 受伤冷却逻辑必须生效,避免敌人被连续秒杀

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:30:08