【问题标题】:More bullets spawn than allowed产生的子弹多于允许的数量
【发布时间】:2017-08-29 20:12:40
【问题描述】:

不知道为什么会发生这种情况,但此代码允许在重新加载后发射 3 颗以上的子弹。我试图找出原因。我认为这可能是检查哪个问题之间的时间,但我可能是错的。

任何帮助将不胜感激。

public bool isFiring;
public bool isReloading = false; 
 public BulletController bullet; // Reference another script
 public float bulletSpeed; // bullet speed
 public float timeBetweenShots; // time between shots can be fired
 private float shotCounter;
 public Transform firePoint;
 public static int ammoRemaining = 3;
 public static int maxAmmo = 3;
 public Transform ammoText;
 // Use this for initialization
 void Awake () {
     isReloading = false;
     ammoRemaining = maxAmmo;
 }

 // Update is called once per frame
 void Update () {
     if(isFiring == true )
     {
         shotCounter -= Time.deltaTime;
         if(shotCounter <= 0 && ammoRemaining > 0 && isReloading == false)
         {
             shotCounter = timeBetweenShots;
             BulletController newBullet = Instantiate(bullet, firePoint.position, firePoint.rotation) as BulletController; // creates a new instance of the bullet
             newBullet.speed = bulletSpeed;
             ammoRemaining -= 1;
             ammoText.GetComponent<Text>().text = "Ammo:" + ammoRemaining;
         }

 }
 else if (ammoRemaining == 0)
 {
     StartCoroutine(Reload());
 }
 else
 {
     shotCounter = 0;
 }
 }
 public IEnumerator Reload()
 {
     isReloading = true;
     ammoText.GetComponent<Text>().text = "REL...";
     yield return new WaitForSeconds(2);
     ammoRemaining = maxAmmo;
     isReloading = false;
     ammoText.GetComponent<Text>().text = "Ammo:" + ammoRemaining;
 }

【问题讨论】:

  • 当您的弹药用完时,您的 Reload() 协程似乎在 Update() 中被多次调用 - 除非设置“isFiring”的任何设置阻止...
  • @ryemoss isFiring 是从另一个脚本设置的,例如 if(Input.GetMouseButtonDown(0)) { playerGun.isFiring = true; } else if (Input.GetMouseButtonUp(0)) { playerGun.isFiring = false; }
  • 所以当你发射最后一个子弹时,你会开始重新加载,但是很多帧会过去,在你放开鼠标按钮之前 Reload() 会被多次调用。这可能不是您的问题的原因 - 但应该以任何一种方式解决。你应该确保在启动协程之前你还没有重新加载。
  • 将此行修改为。 else if (ammoRemaining == 0 &amp;&amp; !isReloading)或者这个:if(isFiring == true &amp;&amp; !isReloading)
  • 不要在Update()里面调用GetComponent&lt;&gt;(),你正在使用的组件每次都是一样的,而不是public Transform ammoText;public Text ammoText,你可以拖动相同统一编辑器中的组件,它会为您执行一次 GetComponent 并存储它。

标签: c# unity3d coroutine reloading


【解决方案1】:

这是一个题外话,不是为了解决您的问题,而是为了避免您将来出现问题,特别是性能方面的问题。如果您打算开发 3D 射击游戏,我建议您不要实例化子弹(除非您打算创建一些延时场景)

原因有两个:

  • 您需要每秒实例化和销毁许多游戏对象
  • 如果你给子弹一个真实的速度,它不应该出现在 场景

我假设为您实例化项目符号的原因主要有以下两个:

  • 创造一些视觉效果,让某物快速离开 按下射击时的枪管
  • 检测您是否使用OnCollisionEnter() 或类似名称击中目标

所以我可以给你一些提示,你可以尝试一些方法来代替实例化子弹。

1- 为了代表拍摄,您可以在枪管末端安装一个灯,在拍摄时它会闪烁。您可以在 Inspector 中选择此灯的颜色、长度、强度...。

在以下脚本中,您有一个示例,说明如何在拍摄期间激活和停用灯光效果。它还控制拍摄之间的时间流逝。没有任何协程。

public Light gunLight; 
public float timeBetweenBullets = 0.15f;

void Update ()
{
    // Add the time since Update was last called to the timer.
    timer += Time.deltaTime;

    // If the Fire1 button is being press and it's time to fire...
    if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets)
    {
        Shoot ();
    }

    // If the timer has exceeded the proportion of timeBetweenBullets that the effects should be displayed for...
    if(timer >= timeBetweenBullets * effectsDisplayTime)
    {
        DisableEffects ();
    }
}

public void DisableEffects ()
{
    // Disable the line renderer and the light.
    gunLight.enabled = false;
}

void Shoot (){
    timer = 0f;
    // Enable the light.
    gunLight.enabled = true;

}

2- 第二部分是如何检测玩家是否朝正确的方向射击。为了解决这个问题,你需要在拍摄时使用raycast,并分析光线击中了什么。

为此,您应该使用以下几行修改上面的 shot 方法:

void Shoot ()
    {
        // Reset the timer.
        timer = 0f;

        // Enable the light.
        gunLight.enabled = true;

        // Set the shootRay so that it starts at the end of the gun and points forward from the barrel.
        shootRay.origin = transform.position;
        shootRay.direction = transform.forward;

        // Perform the raycast against gameobjects on the shootable layer and if it hits something...
        if(Physics.Raycast (shootRay, out shootHit, range, shootableMask))
        {
            // Try and find an EnemyHealth script on the gameobject hit.
            EnemyHealth enemyHealth = shootHit.collider.GetComponent <EnemyHealth> ();

            // If the EnemyHealth component exist...
            if(enemyHealth != null)
            {
                // ... the enemy should take damage.
                enemyHealth.TakeDamage (damagePerShot, shootHit.point);
            }

            // Set the second position of the line renderer to the point the raycast hit.
            gunLine.SetPosition (1, shootHit.point);
        }
        // If the raycast didn't hit anything on the shootable layer...
        else
        {
            // ... set the second position of the line renderer to the fullest extent of the gun's range.
            gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);
        }
    }

现在你的 shoot 方法投射一条射线,如果射线击中敌人(即标记为敌人的 GameObject),它将执行一些动作。在这种特殊情况下,我假设敌人已经附加了一个脚本来减少它被射击时的生命。

您可以让镜头的特殊效果更加复杂,例如:添加声音、渲染线条、一些粒子系统来模拟粉末......

您可以,而且我认为您应该从 Unity 官方网站查看本教程,以更好地理解我刚才在回答中提到的内容,并为您的游戏获得一些额外的想法:

https://unity3d.com/learn/tutorials/projects/survival-shooter/harming-enemies?playlist=17144

【讨论】:

  • 所以在需要生成多个对象时不要实例化
  • 您可以生成对象,这没有问题。例如敌人或任何东西。子弹的问题是你会很快创造出很多东西,而且你几乎看不到它们。所以如果你能用对引擎来说更便宜的其他资源来达到同样的效果,那就更好了
  • 我只允许每隔几秒发射最多 3 发子弹,这会不会产生同样的影响
  • 那样的话我相信不会有太大影响。只是视觉效果通常使用灯光和渲染线比实例化射弹更好。然而,这完全取决于你
【解决方案2】:

问题(如所讨论的)是您的 Reload() 协程在您发射最后一枪之后和释放 MouseButton(0) 之前在 Update() 中被调用了额外的时间。为避免这种情况,我建议在启动协程之前检查以确保您尚未重新加载:

 else if (ammoRemaining == 0 && !isReloading)
 {
     StartCoroutine(Reload());
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-04
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    相关资源
    最近更新 更多