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

如何创建接收相同参数的不同Bullet子类实例?(东方风格游戏)

解决方案:基于工厂模式实现动态子弹生成与冷却管理

核心思路是用抽象工厂+字典注册的方式,把子弹类型、创建逻辑、冷却时间绑定在一起,让角色只需要通过子弹标识(比如"A""B")就能获取冷却时间并触发子弹创建,彻底解耦角色与具体子弹类。

1. 重构抽象基类,统一子弹接口

先把所有子弹的共性抽象出来,强制子类实现自身的冷却时间:

// 所有子弹的抽象基类
public abstract class Attack
{
    public Texture2D Texture { get; protected set; }
    public Vector2 Position { get; protected set; }
    public float Rotation { get; protected set; }
    public float Speed { get; protected set; }
    
    // 每个子弹必须自己定义冷却时间
    public abstract float Cooldown { get; }

    protected Attack(Texture2D texture, Vector2 position, float rotation, float speed)
    {
        Texture = texture;
        Position = position;
        Rotation = rotation;
        Speed = speed;
    }

    public abstract void Update(GameTime gameTime);
}

修改你的BulletA子类,实现抽象属性:

public class BulletA : Attack
{
    // 子类自身的冷却时间(这里用毫秒单位,你可以根据需求调整)
    public override float Cooldown => 500f;

    public BulletA(Texture2D texture, Vector2 position, float rotation, float speed) 
        : base(texture, position, rotation, speed) { }

    public override void Update(GameTime gameTime)
    {
        // 子弹移动逻辑示例
        Position += new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation)) * Speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
    }
}

2. 实现子弹工厂类,统一管理创建与冷却

创建一个静态工厂类,负责注册所有子弹类型,提供获取冷却、创建实例的方法:

public static class BulletFactory
{
    // 存储子弹标识对应的创建逻辑和冷却时间
    private static readonly Dictionary<string, (Func<Texture2D, Vector2, float, float, Attack> Creator, float Cooldown)> _bulletRegistry = new();

    // 注册子弹类型,游戏初始化时调用
    public static void RegisterBullet<T>(string bulletId) where T : Attack
    {
        // 用反射创建子弹实例的委托
        Func<Texture2D, Vector2, float, float, Attack> creator = (tex, pos, rot, spd) => 
            (T)Activator.CreateInstance(typeof(T), tex, pos, rot, spd);
        
        // 临时创建实例获取冷却时间(如果是常量也可以直接读取,这种方式更通用)
        using var tempInstance = (T)Activator.CreateInstance(typeof(T), null, Vector2.Zero, 0f, 0f);
        float cooldown = tempInstance.Cooldown;

        _bulletRegistry[bulletId] = (creator, cooldown);
    }

    // 根据标识获取冷却时间
    public static float GetCooldown(string bulletId)
    {
        if (_bulletRegistry.TryGetValue(bulletId, out var data))
        {
            return data.Cooldown;
        }
        throw new KeyNotFoundException($"找不到标识为{bulletId}的子弹类型");
    }

    // 根据标识创建子弹实例
    public static Attack CreateBullet(string bulletId, Texture2D texture, Vector2 position, float rotation, float speed)
    {
        if (_bulletRegistry.TryGetValue(bulletId, out var data))
        {
            return data.Creator(texture, position, rotation, speed);
        }
        throw new KeyNotFoundException($"找不到标识为{bulletId}的子弹类型");
    }
}

3. 初始化工厂,注册所有子弹类型

在游戏启动阶段(比如Game.LoadContent())注册你需要的子弹:

// 注册BulletA,标识为"A"
BulletFactory.RegisterBullet<BulletA>("A");
// 后续新增BulletB时,直接注册即可
// BulletFactory.RegisterBullet<BulletB>("B");

4. 修改Player类,实现动态切换与冷却控制

Player不再依赖具体子弹类,只需要持有子弹标识列表,通过工厂获取冷却时间:

public class Player : Character
{
    // 存储可切换的子弹标识,比如可以从配置文件读取
    private List<string> _bulletTypeIds = new List<string> { "A" };
    private float _shotCoolDown = 0f;
    // 角色配置的子弹速度
    public float BulletSpeed { get; set; } = 200f;

    protected override void Update(GameTime gameTime)
    {
        var kstate = Keyboard.GetState();
        if(kstate.IsKeyDown(Keys.F)) 
        {
            _shotCoolDown -= (float)gameTime.ElapsedGameTime.TotalSeconds;
            
            if (_shotCoolDown <= 0)
            {
                // 用当前选中的子弹标识射击
                Shoot(_bulletTypeIds[0]);
            }
        }
        base.Update(gameTime);
    }

    public void Shoot(string bulletTypeId)
    {
        // 从工厂获取冷却时间,转成秒(如果Cooldown是毫秒的话)
        _shotCoolDown = BulletFactory.GetCooldown(bulletTypeId) / 1000f;
        
        // 触发事件,传递子弹标识和创建所需的参数
        OnSpriteEvent(new CharacterEventArgs 
        { 
            BulletTypeId = bulletTypeId,
            SpawnPosition = this.Position,
            Rotation = this.Rotation,
            Speed = this.BulletSpeed
        });
    }
}

// 修改事件参数类,传递创建子弹的必要信息
public class CharacterEventArgs : SpriteEventArgs
{
    public string BulletTypeId { get; set; }
    public Vector2 SpawnPosition { get; set; }
    public float Rotation { get; set; }
    public float Speed { get; set; }
}

5. 修改GameEngine的事件处理器,创建子弹实例

在事件处理器中通过工厂创建子弹,交给BulletManager管理:

private void PlayerEventHandler(object sender, SpriteEventArgs args)
{
    var player = sender as Player;
    var characterArgs = args as CharacterEventArgs;
    
    // 加载对应子弹的纹理(这里假设你有统一的资源管理)
    Texture2D bulletTexture = Globals.Content.Load<Texture2D>($"Bullets/Bullet{characterArgs.BulletTypeId}");
    
    // 用工厂创建子弹实例
    Attack bullet = BulletFactory.CreateBullet(
        characterArgs.BulletTypeId,
        bulletTexture,
        characterArgs.SpawnPosition,
        characterArgs.Rotation,
        characterArgs.Speed
    );
    
    // 添加到BulletManager
    BulletManager.AddBullet(bullet);
}

核心优势

  • 彻底解耦:Player不需要知道任何具体子弹类的存在,只需要子弹标识
  • 易于扩展:新增子弹类型时,只需要写子类并在工厂注册,无需修改原有逻辑
  • 冷却统一管理:冷却时间由子弹自身定义,工厂负责统一读取,避免硬编码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:32:16