【Unity框架知识】小众的游戏框架

新星杯·14天创作挑战营·第17期 10w+人浏览 510人参与

小众的Unity游戏框架

1. Entitas(ECS框架) ⭐⭐⭐⭐

小众但被多家大厂魔改使用

// Entitas - 轻量级ECS框架
// 被改造案例:网易、米哈游早期

// 1. 定义组件(纯数据)
public sealed class PositionComponent : IComponent {
    public Vector3 value;
}

public sealed class VelocityComponent : IComponent {
    public Vector3 value;
}

public sealed class HealthComponent : IComponent {
    public int current;
    public int max;
}

// 2. 自动生成的Context(代码生成器)
public sealed class GameContext : Context<GameEntity> {
    public GameContext() : base(
        ComponentsLookup.TotalComponents,0,
        new ContextInfo("Game", ComponentsLookup.componentNames, ComponentsLookup.componentTypes),
        (entity) => new GameEntity()
    ) { }
}

// 3. 系统(System)
public class MovementSystem : IExecuteSystem {
    private readonly IGroup<GameEntity> _group;
    
    public MovementSystem(Contexts contexts) {
        // 只处理有Position和Velocity的实体
        _group = contexts.game.GetGroup(GameMatcher.AllOf(
            GameMatcher.Position,
            GameMatcher.Velocity
        ));
    }
    
    public void Execute() {
        foreach (var entity in _group.GetEntities()) {
            var pos = entity.position.value;
            var vel = entity.velocity.value;
            
            pos += vel * Time.deltaTime;
            entity.ReplacePosition(pos);
        }
    }
}

// 4. 响应式系统(Reactive System)
public class DeathSystem : ReactiveSystem<GameEntity> {
    public DeathSystem(Contexts contexts) : base(contexts.game) { }
    
    protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) {
        // 当Health组件变化时触发
        return context.CreateCollector(GameMatcher.Health);
    }
    
    protected override bool Filter(GameEntity entity) {
        return entity.health.current <= 0;
    }
    
    protected override void Execute(List<GameEntity> entities) {
        foreach (var entity in entities) {
            entity.isDead = true;
            // 播放死亡动画
            // 触发死亡事件
        }
    }
}

// 5. Feature(系统组)
public class GameSystems : Feature {
    public GameSystems(Contexts contexts) {
        // 输入系统
        Add(new InputSystem(contexts));
        
        // 游戏逻辑
        Add(new MovementSystem(contexts));
        Add(new CombatSystem(contexts));
        Add(new AISystem(contexts));
        
        // 响应式系统
        Add(new DeathSystem(contexts));
        Add(new ScoreSystem(contexts));
        
        // 视图系统
        Add(new RenderSystem(contexts));
        Add(new AnimationSystem(contexts));
    }
}

// 6. 游戏入口
public class GameController : MonoBehaviour {
    private Systems _systems;
    private Contexts _contexts;
    
    void Start() {
        _contexts = Contexts.sharedInstance;
        _systems = new GameSystems(_contexts);
        _systems.Initialize();
        
        // 创建实体
        CreatePlayer();
    }
    
    void Update() {
        _systems.Execute();
        _systems.Cleanup();
    }
    
    void CreatePlayer() {
        var player = _contexts.game.CreateEntity();
        player.AddPosition(Vector3.zero);
        player.AddVelocity(Vector3.zero);
        player.AddHealth(100, 100);
        player.isPlayer = true;
    }
}

// 🎮 大厂魔改方向:
// - 米哈游:改造成支持服务器+客户端双端
// - 网易:添加序列化、网络同步
// - 叠纸:增加帧同步支持

为什么大厂喜欢:

  • 性能极高(缓存友好)
  • 代码生成器减少重复代码
  • 容易魔改
  • 适合大规模实体

GitHub: https://github.com/sschmid/Entitas-CSharp


2. Zenject/VContainer(依赖注入) ⭐⭐⭐⭐

被大厂改造成内部DI框架

// Zenject(现改名为Extenject)
// 改造案例:Ubisoft、Supercell改成内部工具

// 1. 定义接口和实现
public interface IWeaponService {
    void Fire();
}

public class GunService : IWeaponService {
    private readonly IAmmoService _ammoService;
    private readonly IAudioService _audioService;
    
    // 构造函数注入
    public GunService(IAmmoService ammoService, IAudioService audioService) {
        _ammoService = ammoService;
        _audioService = audioService;
    }
    
    public void Fire() {
        if (_ammoService.HasAmmo()) {
            _ammoService.ConsumeAmmo();
            _audioService.PlaySound("gunshot");
        }
    }
}

// 2. Installer(注册依赖)
public class GameInstaller : MonoInstaller {
    public override void InstallBindings() {
        // 单例绑定
        Container.Bind<IPlayerService>()
            .To<PlayerService>()
            .AsSingle();
        
        // 工厂绑定
        Container.Bind<IWeaponService>()
            .To<GunService>()
            .AsTransient();
        
        // 从场景绑定
        Container.Bind<PlayerController>()
            .FromComponentInHierarchy()
            .AsSingle();
        
        // 从Resources绑定
        Container.Bind<GameConfig>()
            .FromScriptableObjectResource("Configs/GameConfig")
            .AsSingle();
        
        // 绑定实例
        Container.BindInstance(new GameSettings {
            Difficulty = 2
        }).AsSingle();
        
        // 工厂模式
        Container.BindFactory<Enemy, Enemy.Factory>()
            .FromComponentInNewPrefab(enemyPrefab);
    }
    
    [SerializeField] private GameObject enemyPrefab;
}

// 3. 使用注入
public class PlayerController : MonoBehaviour {
    [Inject] private IWeaponService _weaponService;
    [Inject] private IPlayerService _playerService;
    [Inject] private SignalBus _signalBus;  // 事件总线
    
    void Start() {
        // 注入已经完成,可以直接使用
        _playerService.Initialize();
    }
    
    void Update() {
        if (Input.GetKeyDown(KeyCode.Space)) {
            _weaponService.Fire();
        }
    }
}

// 4. Signal(解耦事件系统)
public class PlayerDiedSignal {
    public int PlayerId;
    public Vector3 Position;
}

public class GameSignalInstaller : Installer<GameSignalInstaller> {
    public override void InstallBindings() {
        // 声明信号
        Container.DeclareSignal<PlayerDiedSignal>();
        
        // 绑定处理器
        Container.BindSignal<PlayerDiedSignal>()
            .ToMethod<UIManager>(x => x.OnPlayerDied)
            .FromResolve();
    }
}

// 发送信号
public class Player : MonoBehaviour {
    [Inject] private SignalBus _signalBus;
    
    void Die() {
        _signalBus.Fire(new PlayerDiedSignal {
            PlayerId = 1,
            Position = transform.position
        });
    }
}

// 5. Memory Pool(对象池集成)
public class BulletPoolInstaller : MonoInstaller {
    public override void InstallBindings() {
        Container.BindMemoryPool<Bullet, Bullet.Pool>()
            .WithInitialSize(50)
            .FromComponentInNewPrefab(bulletPrefab)
            .UnderTransformGroup("Bullets");
    }
}

public class BulletSpawner : MonoBehaviour {
    [Inject] private Bullet.Pool _bulletPool;
    
    void Fire() {
        var bullet = _bulletPool.Spawn();
        bullet.transform.position = firePoint.position;
        
        // 使用后回收
        StartCoroutine(DespawnAfterDelay(bullet, 3f));
    }
    
    IEnumerator DespawnAfterDelay(Bullet bullet, float delay) {
        yield return new WaitForSeconds(delay);
        _bulletPool.Despawn(bullet);
    }
}

// 🏢 魔改点:
// - Supercell:改造成支持热重载
// - Ubisoft:添加多场景管理
// - 某大厂:集成IL2CPP优化版本

VContainer(更轻量的替代)

// VContainer - 性能更好的DI框架
using VContainer;
using VContainer.Unity;

public class GameLifetimeScope : LifetimeScope {
    protected override void Configure(IContainerBuilder builder) {
        // 注册服务
        builder.Register<IPlayerService, PlayerService>(Lifetime.Singleton);
        builder.Register<IWeaponService, GunService>(Lifetime.Transient);
        
        // 注册MonoBehaviour
        builder.RegisterComponentInHierarchy<PlayerController>();
        
        // 注册入口点
        builder.RegisterEntryPoint<GameInitializer>();
    }
}

// EntryPoint(替代MonoBehaviour.Start)
public class GameInitializer : IStartable {
    private readonly IPlayerService _playerService;
    
    public GameInitializer(IPlayerService playerService) {
        _playerService = playerService;
    }
    
    public void Start() {
        _playerService.Initialize();
    }
}

3. Odin Inspector(编辑器增强) ⭐⭐⭐⭐⭐
using Sirenix.OdinInspector;

// 改造案例:很多大厂购买后改成内部工具链
public class WeaponData : ScriptableObject {
    [BoxGroup("基础属性")]
    [LabelText("武器名称")]
    public string weaponName;
    
    [BoxGroup("基础属性")]
    [Range(1, 100)]
    [LabelText("攻击力")]
    public int damage;
    
    [FoldoutGroup("高级设置")]
    [ShowIf("@damage > 50")]
    [LabelText("暴击率")]
    public float criticalRate;
    
    [FoldoutGroup("技能")]
    [TableList(ShowIndexLabels = true)]
    public List<Skill> skills;
    
    [Button("生成配置文件", ButtonSizes.Large)]
    [GUIColor(0.3f, 0.8f, 0.3f)]
    private void GenerateConfig() {
        // 一键生成配置
    }
    
    [PreviewField(100, ObjectFieldAlignment.Center)]
    [BoxGroup("预览")]
    public Sprite icon;
}

[System.Serializable]
public class Skill {
    [HorizontalGroup("Split", 55)]
    [PreviewField(50, ObjectFieldAlignment.Center)]
    [HideLabel]
    public Sprite icon;
    
    [HorizontalGroup("Split")]
    [VerticalGroup("Split/Right")]
    public string skillName;
    
    [VerticalGroup("Split/Right")]
    [ProgressBar(0, 100)]
    public float cooldown;
}

// 🎨 使用案例:
// - 改造成可视化关卡编辑器
// - 基于此做配置表编辑器

4. Behavior Designer(行为树) ⭐⭐⭐⭐
// 改造案例:腾讯、完美世界改成自己的AI系统

using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

// 自定义行为节点
[TaskCategory("Custom/Combat")]
public class FindNearestEnemy : Action {
    [RequiredField]
    public SharedTransform targetEnemy;
    
    public float searchRadius = 10f;
    
    public override TaskStatus OnUpdate() {
        Collider[] colliders = Physics.OverlapSphere(transform.position, searchRadius);
        
        float nearestDistance = float.MaxValue;
        Transform nearest = null;
        
        foreach (var col in colliders) {
            if (col.CompareTag("Enemy")) {
                float distance = Vector3.Distance(transform.position, col.transform.position);
                if (distance < nearestDistance) {
                    nearestDistance = distance;
                    nearest = col.transform;
                }
            }
        }
        
        if (nearest != null) {
            targetEnemy.Value = nearest;
            return TaskStatus.Success;
        }
        
        return TaskStatus.Failure;
    }
}

// 条件节点
[TaskCategory("Custom/Combat")]
public class CanAttack : Conditional {
    public SharedTransform target;
    public float attackRange = 2f;
    
    public override TaskStatus OnUpdate() {
        if (target.Value == null) {
            return TaskStatus.Failure;
        }
        
        float distance = Vector3.Distance(transform.position, target.Value.position);
        return distance <= attackRange ? TaskStatus.Success : TaskStatus.Failure;
    }
}

// 🧠 魔改方向:
// - 加入HTN(分层任务网络)
// - 集成GOAP规划
// - 添加多线程支持
// - 与动画系统深度集成

5. Mirror/FishNet(网络框架) ⭐⭐⭐⭐

开源网络框架,被改造成内部方案

// Mirror - Unity官方UNET的开源替代
// 改造案例:多家独立工作室

using Mirror;

// NetworkBehaviour
public class Player : NetworkBehaviour {
    // [SyncVar] 自动同步
    [SyncVar(hook = nameof(OnHealthChanged))]
    public int health = 100;
    
    [SyncVar]
    public string playerName;
    
    // Command: 客户端 → 服务器
    [Command]
    void CmdFire(Vector3 direction) {
        // 在服务器上执行
        GameObject bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
        
        // 通知所有客户端
        RpcShowFireEffect(direction);
    }
    
    // ClientRpc: 服务器 → 所有客户端
    [ClientRpc]
    void RpcShowFireEffect(Vector3 direction) {
        // 在所有客户端播放特效
        PlayMuzzleFlash();
    }
    
    // TargetRpc: 服务器 → 指定客户端
    [TargetRpc]
    void TargetShowMessage(NetworkConnection target, string message) {
        // 只在目标客户端显示
        UIManager.Instance.ShowMessage(message);
    }
    
    void OnHealthChanged(int oldValue, int newValue) {
        // SyncVar变化时的回调
        UpdateHealthUI(newValue);
    }
    
    void Update() {
        // 只在本地玩家执行
        if (!isLocalPlayer) return;
        
        if (Input.GetKeyDown(KeyCode.Space)) {
            CmdFire(transform.forward);
        }
    }
    
    private GameObject bulletPrefab;
    void PlayMuzzleFlash() { }
    void UpdateHealthUI(int health) { }
}

// NetworkManager
public class GameNetworkManager : NetworkManager {
    public override void OnServerAddPlayer(NetworkConnectionToClient conn) {
        // 服务器添加玩家
        GameObject player = Instantiate(playerPrefab);
        NetworkServer.AddPlayerForConnection(conn, player);
    }
    
    public override void OnClientConnect() {
        // 客户端连接成功
        Debug.Log("已连接到服务器");
    }
    
    private GameObject playerPrefab;
}

// 🌐 魔改案例:
// - 某工作室:改成帧同步
// - 某团队:加入状态同步优化
// - 某公司:集成KCP传输协议

FishNet(更现代的替代)

using FishNet.Object;
using FishNet.Connection;

// FishNet - 性能更好
public class Player : NetworkBehaviour {
    [SyncVar]
    private int health;
    
    [ServerRpc]
    private void Attack(NetworkConnection conn = null) {
        // 服务器RPC
    }
    
    [ObserversRpc]
    private void ShowEffect() {
        // 所有观察者
    }
}

6. DOTween Pro(动画框架) ⭐⭐⭐⭐⭐

几乎所有Unity项目都在用

using DG.Tweening;

public class TweenExamples : MonoBehaviour {
    void Start() {
        // 基础动画
        transform.DOMove(new Vector3(10, 0, 0), 2f);
        
        // 链式调用
        transform.DOScale(2f, 1f)
            .SetEase(Ease.OutBounce)
            .SetDelay(0.5f)
            .OnComplete(() => Debug.Log("完成"));
        
        // Sequence(序列动画)
        Sequence seq = DOTween.Sequence();
        seq.Append(transform.DOMove(Vector3.up * 5, 1f));
        seq.Append(transform.DORotate(Vector3.up * 180, 0.5f));
        seq.Join(transform.DOScale(2f, 0.5f));  // 同时执行
        seq.AppendInterval(1f);  // 等待
        seq.Append(transform.DOMove(Vector3.zero, 1f));
        
        // Path动画
        Vector3[] path = new Vector3[] {
            new Vector3(0, 0, 0),
            new Vector3(5, 0, 0),
            new Vector3(5, 5, 0),
            new Vector3(10, 5, 0)
        };
        transform.DOPath(path, 3f, PathType.CatmullRom);
        
        // Shake效果
        Camera.main.DOShakePosition(0.5f, 0.3f);
        
        // UI动画
        image.DOFade(0, 1f);
        text.DOText("Hello World", 2f);
    }
}

// 🎬 用法:
// - 几乎所有项目的UI动画
// - 技能特效、相机震动
// - 过场动画

7. 小众但强大的框架

A. Stateless(状态机)

// 轻量级状态机,被多家公司集成
using Stateless;

public class EnemyAI : MonoBehaviour {
    enum State { Idle, Patrol, Chase, Attack }
    enum Trigger { SeePlayer, LosePlayer, InRange, OutOfRange }
    
    StateMachine<State, Trigger> _machine;
    
    void Start() {
        _machine = new StateMachine<State, Trigger>(State.Idle);
        
        _machine.Configure(State.Idle)
            .Permit(Trigger.SeePlayer, State.Chase);
        
        _machine.Configure(State.Chase)
            .OnEntry(OnStartChase)
            .Permit(Trigger.InRange, State.Attack)
            .Permit(Trigger.LosePlayer, State.Patrol);
        
        _machine.Configure(State.Attack)
            .OnEntry(OnStartAttack)
            .Permit(Trigger.OutOfRange, State.Chase);
    }
    
    void OnStartChase() { Debug.Log("开始追击"); }
    void OnStartAttack() { Debug.Log("开始攻击"); }
}

B. MessagePack(序列化)

// 被大厂用于网络传输和存档
using MessagePack;

[MessagePackObject]
public class SaveData {
    [Key(0)]
    public int Level { get; set; }
    
    [Key(1)]
    public List<ItemData> Items { get; set; }
}

// 序列化(比JSON快5-10倍)
byte[] bytes = MessagePackSerializer.Serialize(saveData);
SaveData loaded = MessagePackSerializer.Deserialize<SaveData>(bytes);

💡 关键启示

大厂魔改小众框架关键点

开源 = 可控 - 可以完全掌控源码
轻量 = 易改 - 小框架容易理解和修改
专注 = 好用 - 专注解决一个问题
免费 = 省钱 - 开源框架降低成本
社区 = 参考 - 有社区反馈问题


✨随便写写

我们总在寻找什么。找一个故人,一件旧物,或尘封多年的真相。路途充满错过与迷惘,而寻找本身,已在悄然重塑我们。它剥开生活的表象,照见心底真实的感情。有时,最珍贵的并非所求之物,而是路上拾得的自己。

by ONE

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值