【问题标题】:Differentiate Enemy Projectile from User's Projectile区分敌人射弹和用户射弹
【发布时间】:2019-08-09 23:51:44
【问题描述】:

我有一个用户发射弹丸来伤害敌人。

虽然有些敌人有自己的射弹来伤害玩家。它们都来自同一个射弹脚本。

问题在于,当用户升级他的弹丸时(使用游戏内货币)。用户和敌人的弹丸都得到升级。由于它来自同一个脚本,它会更新弹丸的DamageOnHit

现在我的问题是:我能否告诉我的脚本只升级用户的射弹,而不创建单独的敌方射弹脚本?

弹丸代码

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

//[RequireComponent (typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour {

    [Header ("Speed")]
    public float baseSpeed;
    public float randomSpeed;
    public Vector2 SpeedV2;
    public Vector2 Direction;

    [Header ("Damage")]
    public static int DamageOnHit = 1;

    [Header ("Layers")]
    public LayerMask solid_layer;
    public LayerMask entities_layer;

    [Header ("OnHit FX")]
    public GameObject HitFxPrefab;
    public GameObject DustFxPrefab;

    [Header ("Bounce")]
    public bool BounceOnCollide = false;
    public int bouncesLeft = 0;

    [HideInInspector]
    public Health owner; // owner of the projectile
    private Vector2 Position; // Current position
    private Vector2 movementCounter = Vector2.zero;  // Counter for subpixel movement
    public BoxCollider2D myCollider;
    List<Health> healthsDamaged = new List<Health>(); // List to store healths damaged

    void Awake () {
        if (myCollider == null) {
            myCollider = GetComponent<BoxCollider2D> ();
        }
    }

    void Start () {
        // keeping everything Pixel perfect
        Position = new Vector2 (Mathf.Round(transform.position.x), Mathf.Round(transform.position.y));
        transform.position = Position;
    }

    void Update () {
        SpeedV2 = new Vector2 (transform.right.x, transform.right.y) * (baseSpeed + Random.value * randomSpeed) * Time.deltaTime;
    }

    void LateUpdate () {
        if (SpeedV2.x != 0) {
            MoveH (SpeedV2.x);
        }

        if (SpeedV2.y != 0) {
            MoveV (SpeedV2.y);
        }
    }

    void DestroyMe () {
        if (HitFxPrefab != null) {
            var h = Instantiate (HitFxPrefab, transform.position, transform.rotation);
            h.transform.localScale = transform.lossyScale;
            h.transform.localRotation = Quaternion.Euler (new Vector3(0f, 0f, Random.value * 360f));
        }
        Destroy (gameObject);
    }

    void DestroyMeWall () {
        if (HitFxPrefab != null) {
            var h = Instantiate (HitFxPrefab, transform.position, transform.rotation);
            h.transform.localScale = transform.lossyScale;
            h.transform.localRotation = Quaternion.Euler (new Vector3(0f, 0f, Random.value * 360f));
        }
        Destroy (gameObject);
    }

    public void BounceHorizontal () {
        bouncesLeft--;
        transform.right = new Vector3 (-transform.right.x, transform.right.y, transform.right.z);
        SpeedV2 *= 0.8f;
    }

    public void BounceVertical () {
        bouncesLeft--;
        transform.right = new Vector3 (transform.right.x, -transform.right.y, transform.right.z);
        SpeedV2 *= 0.8f;
    }

    void OnCollideWith (Collider2D col, bool horizontalCol = true) {
        var component = col.GetComponent<Health> ();
        // If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
        if (component != null && component != owner && !healthsDamaged.Contains(component)) {
            // Add the health component to the list of damaged healths
            healthsDamaged.Add (component);

            // Apply the damage
            var didDamage = component.TakeDamage (DamageOnHit);
            // Destroy the projectile after applying damage
            if (didDamage) {
                DestroyMe ();
                return;
            }
        }

        // if the projectile hit's a solid object, destroy it
        if (col.gameObject.layer ==  (int)Mathf.Log(solid_layer.value, 2)) {
            DestroyMeWall ();
            return;
        }
    }

    void OnCollideWithEntity(Collider2D col) {
        var component = col.GetComponent<Health> ();
        // If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
        if (component != null && component != owner && !healthsDamaged.Contains(component)) {
            // Add the health component to the list of damaged healths
            healthsDamaged.Add (component);

            // Apply the damage
            var didDamage = component.TakeDamage (DamageOnHit);
            // Destroy the projectile after applying damage
            if (didDamage) {
                DestroyMe ();
            }
        }
    }


    // Function to move the Actor Horizontally, this only stores the float value of the movement to allow for subpixel movement and calls the MoveHExact function to do the actual movement
    public bool MoveH(float moveH) {
        this.movementCounter.x = this.movementCounter.x + moveH;
        int num = (int)Mathf.Round(this.movementCounter.x);
        if (num != 0)
        {
            this.movementCounter.x = this.movementCounter.x - (float)num;
            return this.MoveHExact(num);
        }
        return false;
    }

    // Function to move the Actor Horizontally, this only stores the float value of the movement to allow for subpixel movement and calls the MoveHExact function to do the actual movement
    public bool MoveV(float moveV) {
        this.movementCounter.y = this.movementCounter.y + moveV;
        int num = (int)Mathf.Round(this.movementCounter.y);
        if (num != 0)
        {
            this.movementCounter.y = this.movementCounter.y - (float)num;
            return this.MoveVExact(num);
        }
        return false;
    }

    // Function to move the Actor Horizontally an exact integer amount
    public bool MoveVExact(int moveV) {
        int num = (int)Mathf.Sign((float)moveV);
        while (moveV != 0) {
            bool solid = CheckColInDir(Vector2.up * (float)num, solid_layer);
            if (solid) {
                if (BounceOnCollide && bouncesLeft > 0) {
                    bouncesLeft--;
                    num = -num;
                    moveV = -moveV;
                    BounceVertical ();
                } else {
                    this.movementCounter.x = 0f;
                    DestroyMeWall ();
                    return true;
                }
            }

            bool entity = CheckColInDir(Vector2.up * (float)num, entities_layer);
            if (entity) {
                var entit = CheckColsInDirAll (Vector2.up * (float)num, entities_layer);
                OnCollideWithEntity (entit [0]);
            }

            moveV -= num;
            transform.position = new Vector2 (transform.position.x, transform.position.y + (float)num);
        }
        return false;
    }


    // Function to move the Actor Horizontally an exact integer amount
    public bool MoveHExact(int moveH) {
        int num = (int)Mathf.Sign((float)moveH);
        while (moveH != 0) {
            bool solid = CheckColInDir(Vector2.right * (float)num, solid_layer);
            if (solid) {
                if (BounceOnCollide && bouncesLeft > 0) {
                    bouncesLeft--;
                    num = -num;
                    moveH = -moveH;
                    BounceHorizontal ();
                } else {
                    this.movementCounter.x = 0f;
                    DestroyMeWall ();
                    return true;
                }
            }

            bool entity = CheckColInDir(Vector2.right * (float)num, entities_layer);
            if (entity) {
                var entit = CheckColsInDirAll (Vector2.right * (float)num, entities_layer);
                OnCollideWithEntity (entit [0]);
            }

            moveH -= num;
            transform.position = new Vector2 (transform.position.x + (float)num, transform.position.y);
        }
        return false;
    }

    // Helper function to check if there is any collision within a given layer in a set direction (only use up, down, left, right)
    public bool CheckColInDir (Vector2 dir, LayerMask layer) {
        Vector2 leftcorner = Vector2.zero;
        Vector2 rightcorner = Vector2.zero;

        if (dir.x > 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
            rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x + .5f, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
        } else if (dir.x < 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x - .5f, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
            rightcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
        } else if (dir.y > 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y + .5f);
            rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y);
        } else if (dir.y < 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y);
            rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y - .5f);
        }

        return Physics2D.OverlapArea(leftcorner, rightcorner, layer);
    }


    // The same as CheckColInDir but it returns a Collider2D array of the colliders you're collisioning with
    public Collider2D[] CheckColsInDirAll (Vector2 dir, LayerMask layer) {
        Vector2 leftcorner = Vector2.zero;
        Vector2 rightcorner = Vector2.zero;

        if (dir.x > 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
            rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x + .5f, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
        } else if (dir.x < 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x - .5f, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
            rightcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
        } else if (dir.y > 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y + .5f);
            rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y);
        } else if (dir.y < 0) {
            leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y);
            rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y - .5f);
        }

        return Physics2D.OverlapAreaAll(leftcorner, rightcorner, layer);
    }
}

升级菜单代码。如您所见,我从弹丸脚本升级了 DamageOnHit。这就是所有射弹受到更多伤害的原因。


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

public class UpgradeMenu : MonoBehaviour
{
    [SerializeField]
    private Text accuracyText;

    [SerializeField]
    private Text speedText;

    [SerializeField]
    private Text damageText;

  [SerializeField]
  private Weapon weapon;

  [SerializeField]
  public Projectile projectile;

  [SerializeField]
  private Player player;

  [SerializeField]
  private int upgradeCost = 50;

    void start ()
    {

    }

    void OnEnable()
    {
        UpdateValues();
    }

    void UpdateValues ()
    {

    }

    public void UpgradeArmor ()
    {
      Health.maxHealth += 2;

    ScoreManager.Score -= upgradeCost;

      UpdateValues();
    }

    public void UpgradeSouls ()
    {
      EnemySlime.ScoreOnDeath += 1;
      EnemySkeleton.ScoreOnDeath += 1;

    //  ScoreManager.Score -= upgradeCost;
      UpdateValues();
    }

    public void UpgradeDamage ()
    {
      Projectile.DamageOnHit += 1;

    //  ScoreManager.Score -= upgradeCost;

      UpdateValues();
    }
}


【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    在您想要实现的目标中,您希望避免使用静态字段。 你应该能够使用类似的东西:

    player.projectile = new  Projectile();
    player.projectile.DamageOnHit = 5;
    
    enemy.projectile = new  Projectile(); 
    enemy.projectile.DamageOnHit = 1;
    

    希望对你有帮助

    【讨论】:

      【解决方案2】:

      @YouneS 回答涉及使用基类,这是一个更好的解决方案,但它确实意味着您总共需要 3 个类。 1 个基类持有标准射弹逻辑,2 个其他类用于继承射弹基类的玩家和敌方射弹。如果您不知道什么是继承,这里有一个链接:https://www.tutorialspoint.com/csharp/csharp_inheritance

      还有一种不同的方法。您可以像这样在 projectile 类中创建一个名为 ProjectileType 的枚举:

      public Enum ProjectileType { PlayerProjectile, EnemyProjectile }
      

      每当您像这样创建/实例化一个射弹时,将此 ProjectileType 分配给一个 projectileType 变量:

      private projectileType;
      
      public Projectile(ProjectileType projectileType)
      {
          this.projectileType = projectileType;
      }
      

      现在,每当您升级射弹时,您都可以像这样检查射弹是否属于玩家类型:

      if(projectileType == ProjectileType.PlayerProjectile)
      {
          // Upgrade logic.
      }
      

      我仍然认为 YouneS 的答案更聪明,但这完全取决于你!

      【讨论】:

      • 感谢您的澄清和替代方案
      • 没问题! @YouneS
      【解决方案3】:

      我不确定这是否是最佳解决方案,因为这完全取决于您的游戏逻辑,但您可以创建两类射弹,一类用于敌人,一类用于玩家。这两个类都可以继承自您的 Projectile 类。

      public class PlayerProjectil : Projectile {...}
      public class AIProjectil : Projectile {...}
      

      使 Projectile 类函数虚拟化。当您需要不同的行为时,并在 PlayerProjectil 和 AIProjectil 中覆盖它们。

      【讨论】:

      • 问题是,我尝试复制弹丸类并将其命名为其他名称。调整我的脚本。这样做的问题是敌人没有射击。所以这就是为什么我宁愿把所有东西都放在一个工作脚本里。
      • 我面临的一个问题是“DamageOnHit”必须是一个公共静态整数,因为我需要能够使用我的 UpgradeMenu 脚本访问这个类。而且我猜没有“Public virtual static int”。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多