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

如何在Unity中将C#类用作GameObject?OOP架构问题求助

Unity多敌人类型生成的OOP设计问题

我感觉自己没搞懂OOP的一个核心概念,想请教一下。

我在Unity里用Enemy Prefab和mobSpawner脚本实现了基础敌人生成,现在要创建多个从同一敌人基类派生的敌人类型。一开始把spawnRate和spawnTimer放在MobSpawner脚本里,现在想把这些变量移到单独的敌人脚本里,但移完之后没法在Unity里分配Mob了——因为它们不是GameObject。

原MobSpawner代码:

public class mobSpawner : MonoBehaviour
{

    //declare mob and mob2 classes
    public MobScript mob1 = new Mob1();
    public Mob2Script mob2 = new Mob2();


    //old way of spawning
    // public float mob1SpawnRate = 3;
    // public float mob1Timer = 0;
    // public float mob2SpawnRate = 10;
    // public float mob2Timer = 0;
    
    private Vector2 screenBounds;
    private float spawnNum = 1;

    
    void Start()
    {
        screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
    }

    // Update is called once per frame
    void Update()
    {
        //mob spawner
        if(mob1.timer < mob1.spawnRate && playerScr.frogAlive){
          mob1.timer = mob1.timer + Time.deltaTime;
        }
        else if(playerScr.frogAlive){
            spawnMob();
            mob1.timer = 0;
            print("spawn");
        }

    }

    public void spawnMob(){
        if(spawnNum < 2){
            Instantiate(mob1, new Vector3(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y), 0), transform.rotation);
            spawnNum++;
        }
        else{
            Instantiate(mob1, new Vector3(screenBounds.x * 2, Random.Range(-screenBounds.y, screenBounds.y), 0), transform.rotation);
            spawnNum--;
        }
        
    }

    public void spawnMob2(){
        if(spawnNum < 2){
            Instantiate(mob2, new Vector3(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y), 0), transform.rotation);
            spawnNum++;
        }
        else{
            Instantiate(mob2, new Vector3(screenBounds.x * 2, Random.Range(-screenBounds.y, screenBounds.y), 0), transform.rotation);
            spawnNum--;
        }
        
    }

}

我希望把spawnRate移到独立脚本里,给每种敌人设置独特的spawnRate、spawnTimer还有后续的生命值等属性,现在的敌人脚本是这样的:

public class MobScript : MonoBehaviour
{
    public float spawnRate = 3;
    public float timer = 0;

}


public class Mob2Script : MonoBehaviour
{
    public float spawnRate = 15;
    public float timer = 0;
}

public class Mob1: MobScript
{

}

public class Mob2: Mob2Script
{

}

核心问题分析

你现在的问题出在混淆了「普通C#类实例」和「Unity预制体(GameObject)」:

  • 直接用new Mob1()创建的是脱离Unity GameObject体系的纯C#对象,既没法用Instantiate生成,也没法在Inspector面板配置。
  • 生成相关的计时器、速率属于生成器的逻辑,不该放在敌人脚本里——未生成的敌人不需要运行计时器,计时器应该和生成行为绑定。

正确实现方案

1. 重构敌人基类与派生类

统一敌人基类,只保留敌人自身的属性(如生命值、移动速度),将生成相关参数剥离到生成器:

// 敌人基类:封装所有敌人共有的属性与行为
public class BaseMob : MonoBehaviour
{
    public int health = 100; // 基础生命值
    public float moveSpeed = 2f; // 基础移动速度
}

// 第一种敌人:继承基类,可自定义自身属性
public class Mob1 : BaseMob
{
    void Start()
    {
        health = 80; // 自定义Mob1的生命值
    }
}

// 第二种敌人:继承基类,扩展自身特性
public class Mob2 : BaseMob
{
    void Start()
    {
        moveSpeed = 3f; // 自定义Mob2的移动速度
    }
}

2. 重构Spawner脚本

用可序列化的配置类,将「敌人预制体」和「生成参数」配对管理,支持在Inspector直观配置:

using UnityEngine;

// 序列化类:用于在Inspector配置每种敌人的生成规则
[System.Serializable]
public class MobSpawnConfig
{
    public BaseMob mobPrefab; // 拖入对应的敌人预制体
    public float spawnRate = 3f; // 生成间隔
    [HideInInspector] public float spawnTimer = 0f; // 生成计时器,无需在Inspector显示
}

public class MobSpawner : MonoBehaviour
{
    public MobSpawnConfig[] mobConfigs; // 所有敌人的生成配置数组
    private Vector2 screenBounds;
    private int spawnSide = 1; // 控制左右侧生成切换

    void Start()
    {
        screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
    }

    void Update()
    {
        if (!playerScr.frogAlive) return;

        // 遍历所有敌人配置,统一处理生成逻辑
        foreach (var config in mobConfigs)
        {
            config.spawnTimer += Time.deltaTime;
            if (config.spawnTimer >= config.spawnRate)
            {
                SpawnMob(config.mobPrefab);
                config.spawnTimer = 0f;
                Debug.Log($"Spawned {config.mobPrefab.name}");
            }
        }
    }

    void SpawnMob(BaseMob mobPrefab)
    {
        float spawnX = spawnSide == 1 ? screenBounds.x * 2 : screenBounds.x * -2;
        Vector3 spawnPos = new Vector3(spawnX, Random.Range(-screenBounds.y, screenBounds.y), 0);
        Instantiate(mobPrefab, spawnPos, transform.rotation);
        spawnSide = spawnSide == 1 ? 2 : 1; // 切换生成侧
    }
}

3. Unity编辑器配置步骤

  1. 给每个敌人预制体挂载对应的派生类脚本(比如Mob1预制体挂Mob1脚本,Mob2预制体挂Mob2脚本)。
  2. 在MobSpawner所在的GameObject的Inspector面板,调整mobConfigs数组大小,每个元素拖入对应敌人预制体,设置好spawnRate即可。

关键OOP概念梳理

  • 封装:敌人自身属性封装在BaseMob及其派生类,生成逻辑封装在MobSpawner,职责清晰分离。
  • 继承:所有敌人继承BaseMob,复用基础属性,同时可扩展自身独有的特性。
  • 多态:Spawner通过BaseMob类型引用所有敌人预制体,统一处理生成逻辑,后续新增敌人只需继承BaseMob、添加新的SpawnConfig,无需修改Spawner核心代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:55:17