mafiaf
mafiaf

Reputation: 99

Modify prefab ingame, revert back when game ends

In my game, there are multiple types of projectiles. I have projectiles from the player and also for some enemies. But all these projectiles are directed from the same script.

In order for each projectile to behave differently, I made a prefab for each which instantiates when that "weapon" triggers it. This way each prefab has its own statistics of damage and other components.

Inside the game I want the user to be able to change the prefabs "Damage on hit", but this needs to be reverted back once the user is dead and restarts the game.

So my question is, within my upgrade menu how can I change the value of DamageOnhit for each different projectile since these are all attached to different weapons.

My 3 different projectile prefabs (Going to be more in the future) Prefabs

The int counter of each prefab Damage

My projectile causes each different projectile to start with standard damage of 1.

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 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);
    }
}


UpgradeMenu


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

    //  ScoreManager.Score -= upgradeCost;

      UpdateValues();
    }

I want to be able to upgrade this value for each different prefab. I tried this with changing the DamageOnHit to a static, but once I upgrade this value. All the projectiles get upgraded. This is NOT what I want since I want to individually be able to change each prefab.

Upvotes: 0

Views: 130

Answers (2)

Chochosan
Chochosan

Reputation: 156

You create a BulletManager.cs (attach this to an object that will be always active or an empty gameobject)

public static BulletManager instance;
private void Awake
{
  if ( instance == null ) //this creates a Singleton so you can access it directly from everywhere, won't go deep into explaining how it works exactly
    {
        instance = this;
    }
    else
    {
        Destroy (gameObject);
    }
}

public int machinegunDmg; //set the initial values in the editor
public int shotgunDmg;
public int skeletonDmg;

Now tag all your prefabs with appropriate tags, let's say you use a "MachineGunProj" tag for the machinegun projectile prefab.

The same script you have attached to all prefabs should get the damage from that BulletManager script depending on what prefab you instantiate.

private int DamageOnHit;  
//this will get called everytime you instantiate a new prefab that holds this script; it will check for its own tag and depending on it will set the damage in this script to be equal to the appropriate value from BulletManager.cs
private void Start
{
    if(this.gameObject.CompareTag("MachineGunProj"))
    {
      this.DamageOnHit = BulletManager.instance.machinegunDmg;
    }
    else if(this.gameObject.CompareTag("ShotgunProj"))
    {
      this.DamageOnHit = BulletManager.instance.shotgunDmg;
    }
    //else if -- do the same for every prefab you have
}

As for the upgrades, you would need to change the values in BulletManager.cs. For example:

public void UpgradeMachineGun()
{
   BulletManager.instance.machinegunDmg++; //next time you spawn a machinegun prefab it will take the upgraded value
}

*I wrote the code above directly here without the assistance of any text editors or whatever so there is a chance that I have probably missed something but overall this is the idea of how it should work. If something doesn't work I would be more than glad to give you further assistance :)

Upvotes: 1

hugo
hugo

Reputation: 3231

Two alternatives:

  • Set DamageOnHit on each instance of the projectile

    Each time you Instantiate a new projectile prefab, get its Projectile component and set its DamageOnHit to the desired value.
    .

  • Each time the game restarts, make a copy of each prefab asset

    We will call them "ProjectileShotgunProto" and "ProjectileSkeletonProto". You will call Instantiate(ProjectileShotgunProto) them when the player shoots, instead of instantiating your original prefab.

In any case, do not alter the original prefab assets from your code, that will lead to issues.

Upvotes: 1

Related Questions