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

Unity中ProjectilePool自动发射及攻击失效问题求助

Unity投射物对象池问题解决方案

问题概述

进入Play模式时投射物自动发射,调整设置后又出现对象池无法激活、玩家攻击功能失效的问题。

核心问题根源

  1. 自动发射原因:对象池取出投射物后未从列表移除,同一实例被重复调用移动方法;且归还时未重置投射物的moves状态,导致回到池后仍持续移动。
  2. 对象池失效原因:投射物初始化时未禁用,归还时未重置状态、未禁用GameObject,导致池中实例状态混乱;取出逻辑未移除列表元素,造成实例重复使用和列表膨胀。

分步修复方案

1. 修复对象池核心逻辑(ProjectilePooling.cs)

修正取出/归还逻辑,初始化时禁用投射物,确保实例状态正确:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProjectilePooling : MonoBehaviour
{
    [SerializeField]
    float poolLength;
     
    public GameObject projectilePrefab;

    private List<Projectile> projectilesInPool;

    public static ProjectilePooling Instance;

    private void Awake()
    {
        Instance = this;
    }

    void Start()
    {        
        PoolInitialization();       
    }

    public Projectile GetProjectile(Vector3 pos, Quaternion rot)
    {
        // 池为空时临时补充实例,避免报错
        if (projectilesInPool.Count == 0)
        {
            GameObject _projectile = Instantiate(projectilePrefab, transform.position, transform.rotation);
            Projectile proj = _projectile.GetComponent<Projectile>();
            proj.gameObject.SetActive(false);
            projectilesInPool.Add(proj);
        }

        Projectile _projectile = projectilesInPool[0];
        projectilesInPool.RemoveAt(0); // 从池中移除实例
        
        _projectile.gameObject.SetActive(true);
        _projectile.transform.position = pos;
        _projectile.transform.rotation = rot;
        _projectile.ResetState(); // 重置投射物状态
        
        return _projectile;
    }

    public void ReturnToPool(Projectile _projectile)
    {
        _projectile.gameObject.SetActive(false);
        _projectile.transform.position = transform.position;
        projectilesInPool.Add(_projectile);
    }

    void PoolInitialization()
    {
        projectilesInPool = new List<Projectile>();
       
        for (int i = 0; i < poolLength; i++)
        {
            GameObject _projectile = Instantiate(projectilePrefab, transform.position, transform.rotation);
            Projectile proj = _projectile.GetComponent<Projectile>();
            proj.gameObject.SetActive(false); // 初始化时禁用
            projectilesInPool.Add(proj);
        }
    }
}

2. 重置投射物状态(Projectile.cs)

添加状态重置方法,确保每次取出时都是初始状态:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Projectile : MonoBehaviour
{
    private Vector3 firingPoint;

    [SerializeField]
    private float projectileSpeed;

    [SerializeField]
    private float maxProjectileDistance;

    private bool moves = false;

    void Start()
    {
        firingPoint = transform.position;
    }

    void Update()
    {
        if (moves)
        {
            MovingTheProjectile();
        }
    }

    public void StartMovement()
    {
        moves = true;
        firingPoint = transform.position; // 记录当前发射位置
    }

    void MovingTheProjectile()
    {
        if (Vector3.Distance(firingPoint, transform.position) > maxProjectileDistance)
        {
            ProjectilePooling.Instance.ReturnToPool(this);
        }
        else
        {
            transform.Translate(Vector3.forward * projectileSpeed * Time.deltaTime);
        }
    }

    public void ResetState()
    {
        moves = false;
        firingPoint = Vector3.zero;
        // 若有刚体组件,可添加rigidbody.velocity = Vector3.zero;
    }

    // 对象禁用时自动重置状态
    private void OnDisable()
    {
        ResetState();
    }
}

3. 适配攻击逻辑(PlayerAttackPower.cs)

更新调用方法,确保与修复后的对象池兼容:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerAttackPower : MonoBehaviour
{
    [SerializeField]
    Transform firingPoint;
    
    [SerializeField]
    float firingSpeed;

    public static PlayerAttackPower Instance;

    private float lastShootingTime = 0;

    void Awake()
    {
        Instance = this;
    }

    public void Shoot()
    {
        if (lastShootingTime + firingSpeed <= Time.time)
        {           
            Projectile _projectile = ProjectilePooling.Instance.GetProjectile(firingPoint.position, firingPoint.rotation);
            _projectile.StartMovement();
            lastShootingTime = Time.time;
        }
    }
}

4. 清理玩家脚本(PlayerScript.cs)

移除无用的静态变量,简化攻击触发逻辑:

using Mirror;
using UnityEngine;

namespace QuickStart
{
    public class PlayerScript : NetworkBehaviour
    {     
        void Update()
        {
            if (!isLocalPlayer) { return; }

            float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110.0f;
            float moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4f;

            transform.Rotate(0, moveX, 0);
            transform.Translate(0, 0, moveZ);
            InputShootingManager();
        }

        void InputShootingManager()
        {
            if (Input.GetButton("Fire1"))
            {
                PlayerAttackPower.Instance.Shoot();               
            }
        }
    }
}

关键修复总结

  • 移除了冗余的isActiveObj静态变量,避免状态干扰
  • 对象池初始化时禁用所有投射物,保证初始状态正确
  • 取出投射物时从列表移除,归还时添加回列表,确保对象池逻辑闭环
  • 添加状态重置机制,彻底清除投射物的旧状态
  • 优化方法和变量命名,提升代码可读性和维护性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:45:53