Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Creating an Autofire method with changable weapons (Space Invaders Clone)

Discussion in 'Scripting' started by saberwolfcdw, Oct 29, 2015.

  1. saberwolfcdw

    saberwolfcdw

    Joined:
    Sep 21, 2015
    Posts:
    14
    I am trying to create a method that or series of methods that allows a ship to shoot with out any key being pressed and will change depending on what the current weapon is. I am thinking a would need a recursive method but I just can't wrap my head around how to get it to work. Here is my whole Ship code. Currently the space bar can be pressed to fire and 1-3 for weapon select. AutoFire1() does work because it is called from Start() but I cant get how to get it to change weapons. Anyone have any ideas? Any insight would be great. Thanks!

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ShipController : MonoBehaviour {
    6.  
    7.     public float moveRate = 10f;
    8.     public GameObject projectile;
    9.     float xMin;
    10.     float xMax;
    11.     float yMin;
    12.     float yMax;
    13.     public float projectilespeed;
    14.     public float firingRate;
    15.     public float playerHealth;
    16.     public int currentWeapon = 1;
    17.     private LevelManager levelManager;
    18.     public AudioClip laserShot;
    19.     public GameObject thruster;
    20.     //public bool gameStarted = false;
    21.    
    22.    
    23.    
    24.     // Use this for initialization
    25.     void Start () {
    26.         float distance = transform.position.z - Camera.main.transform.position.z;
    27.         Vector3 bottomleftmost = Camera.main.ViewportToWorldPoint(new Vector3(.04f,.04f,distance));
    28.         Vector3 toprightmost = Camera.main.ViewportToWorldPoint(new Vector3(.96f,.3f,distance));
    29.         Vector3 startpos = Camera.main.ViewportToWorldPoint(new Vector3(.5f,.1f,distance));
    30.         xMin = bottomleftmost.x;
    31.         yMin = bottomleftmost.y;
    32.         xMax = toprightmost.x;
    33.         yMax = toprightmost.y;
    34.         this.transform.position = startpos;
    35.         levelManager = GameObject.FindObjectOfType<LevelManager>();
    36.         Vector3 thrusterStartPosition = transform.position + new Vector3 (0, -0.45f, 0);
    37.         GameObject playerShipThruster = Instantiate(thruster, thrusterStartPosition, Quaternion.identity) as GameObject;
    38.         AutoFire();
    39.     }
    40.    
    41.    
    42.     // Update is called once per frame
    43.     void Update () {
    44.    
    45.         //if(Input.GetKeyDown(KeyCode.Space)){
    46.         //    gameStarted = true;
    47.         //}
    48.        
    49.         if(Input.GetKey(KeyCode.LeftArrow)) {
    50.             transform.position += Vector3.left * moveRate * Time.deltaTime;
    51.         }
    52.         else if (Input.GetKey(KeyCode.RightArrow)) {
    53.             transform.position += Vector3.right * moveRate * Time.deltaTime;
    54.         }
    55.         if (Input.GetKey(KeyCode.UpArrow)) {
    56.             transform.position += Vector3.up * moveRate * Time.deltaTime;
    57.         }
    58.         else if (Input.GetKey(KeyCode.DownArrow)) {
    59.             transform.position += Vector3.down * moveRate * Time.deltaTime;
    60.         }
    61.        
    62.         float newX = Mathf.Clamp(transform.position.x, xMin, xMax);
    63.         float newY = Mathf.Clamp(transform.position.y, yMin, yMax);
    64.        
    65.         transform.position = new Vector3 (newX, newY, transform.position.z);
    66.        
    67.        
    68.        
    69.         if(Input.GetKeyDown(KeyCode.Alpha1)){
    70.             currentWeapon = 1;
    71.         }
    72.        
    73.         if(Input.GetKeyDown(KeyCode.Alpha2)){
    74.             currentWeapon = 2;
    75.         }
    76.         if(Input.GetKeyDown(KeyCode.Alpha3)){
    77.             currentWeapon = 3;
    78.         }
    79.         if(Input.GetKeyDown(KeyCode.Space)){
    80.         //if(gameStarted){
    81.             if(currentWeapon == 1){
    82.                 InvokeRepeating("FireLaser1", 0.00001f, firingRate);
    83.             }
    84.             if(currentWeapon == 2){
    85.                 InvokeRepeating("FireLaser1", 0.00001f, (firingRate * .5f));
    86.             }
    87.             if(currentWeapon == 3){
    88.                 InvokeRepeating("FireLaser2", 0.00001f, (firingRate));
    89.             }
    90.         }
    91.         if(Input.GetKeyUp(KeyCode.Space)){
    92.             if(currentWeapon == 1){
    93.                 CancelInvoke("FireLaser1");
    94.             }
    95.             if(currentWeapon == 2){
    96.                 CancelInvoke("FireLaser1");
    97.             }
    98.             if(currentWeapon == 3){
    99.                 CancelInvoke("FireLaser2");
    100.             }
    101.         }
    102.        
    103.  
    104.         //if (playerHealth <= 0){
    105.         //    levelManager.GameLost();
    106.         //}
    107.     }
    108.    
    109.     void OnTriggerEnter2D(Collider2D collider){
    110.         Projectile laser = collider.gameObject.GetComponent<Projectile>();
    111.         if (laser){
    112.             playerHealth -= laser.damageCaused();
    113.             laser.Hit();
    114.             if (playerHealth <=0){
    115.                 levelManager.GameLost();
    116.                 //Destroy(gameObject);
    117.             }
    118.             Debug.Log ("Hit by a Laser");
    119.             Destroy(laser);
    120.         }
    121.     }
    122.    
    123.     void FireLaser1(){
    124.         Vector3 startPosition = transform.position + new Vector3 (0, 0.6f, 0);
    125.         GameObject laser = Instantiate(projectile, startPosition, Quaternion.identity) as GameObject;
    126.         laser.GetComponent<Rigidbody2D>().velocity = new Vector3(0,projectilespeed,0);
    127.         AudioSource.PlayClipAtPoint(laserShot, transform.position);
    128.     }
    129.    
    130.     void FireLaser2(){
    131.         Vector3 startPosition1 = transform.position + new Vector3 (-0.3f, 0.6f, 0);
    132.         GameObject laser1 = Instantiate(projectile, startPosition1, Quaternion.identity) as GameObject;
    133.         laser1.GetComponent<Rigidbody2D>().velocity = new Vector3(0,projectilespeed,0);
    134.         AudioSource.PlayClipAtPoint(laserShot, transform.position);
    135.         Vector3 startPosition2 = transform.position + new Vector3 (0.3f, 0.6f, 0);
    136.         GameObject laser2 = Instantiate(projectile, startPosition2, Quaternion.identity) as GameObject;
    137.         laser2.GetComponent<Rigidbody2D>().velocity = new Vector3(0,projectilespeed,0);
    138.         AudioSource.PlayClipAtPoint(laserShot, transform.position);
    139.     }
    140.    
    141.     void AutoFire1(){
    142.         if(currentWeapon == 1){
    143.             InvokeRepeating("FireLaser1", 0.00001f, firingRate);
    144.         }
    145.     }
    146. }
    147.  
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Disassociate the 'weapon' from the controller.

    Put them in their own classes that share some similar interface. Here I'll do it explicitly with an 'interface':

    Code (csharp):
    1.  
    2. public interface IWeapon
    3. {
    4.     void Fire();
    5. }
    6.  
    7. public class LaserType1 : MonoBehaviour, IWeapon
    8. {
    9.     public void Fire()
    10.     {
    11.         //do what you do in FireLaser1
    12.     }
    13. }
    14.  
    15. public class LaserType2 : MonoBehaviour, IWeapon
    16. {
    17.  
    18.     public void Fire()
    19.     {
    20.         //do what you do in FireLaser2
    21.     }
    22.  
    23. }
    24.  
    Now attach those scripts to the same GameObject as ShipController.

    Create a 'currentWeapon' property in ShipController.

    Then you can swap the value of that variable whenever you change weapons. And the autofire would just say:

    Code (csharp):
    1.  
    2. currentWeapon.Fire();
    3.  
    You can then add and remove available weapons by adding and removing them as components of the GameObject.

    You could even have them on GameObjects as children of that GameObject, if the weapon needs to be such. For example a weapon that is a turret on the front of the ship, vs one that is hanging from the wing.

    All that's needed is you need to give ShipController a reference to each... such as members of an array.
     
  3. saberwolfcdw

    saberwolfcdw

    Joined:
    Sep 21, 2015
    Posts:
    14
    Ok. This is a lot for me to process. I am still rather new to coding. I need to take it step by step.

    So you are saying create a new script that is just for weapons and have every weapon set out as its own class with in that script.

    Next thing Interface IWeapon. Interfaces are new to me. Hadn't seen them yet. Didn't really get any info from the Unity Help docs on it. Any info on what they do?
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    An interface is basically a way to tell the compiler "You should expect this class to have X, Y, and Z public functions". That's the first code block in the above post - just a description of what that interface is going to look like. (Note that, because an interface is by its nature public, you don't need the 'public' keyword there - but you do need it when you write the functions in the actual class.) They can often be used just like classes after you write them, including in GetComponent calls - it'll look for any class that implements the interface. Building off of the example above:

    Code (csharp):
    1. IWeapon theWeaponIHave = GetComponent<IWeapon>();
    2. theWeaponIHave.Fire();
    The code above will fire any LaserType1 or LaserType2 weapons you have attached. The compiler knows that those classes are going to have a Fire() method because the inclusion of the interface (look after ": MonoBehaviour") says so - it'll throw an error if you use the interface but don't write a Fire() method.
     
  5. saberwolfcdw

    saberwolfcdw

    Joined:
    Sep 21, 2015
    Posts:
    14
    ok so I currently have a Weapon Script

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5.  
    6.  
    7. public interface IWeapon
    8. {
    9.     void Fire();
    10. }
    11.  
    12. public class Laser1 : MonoBehaviour, IWeapon
    13. {
    14.    
    15.     private ShipController playership;
    16.     public GameObject laser1;
    17.     public float projectilespeed;
    18.     public AudioClip laserShot;
    19.    
    20.     public void Fire()
    21.     {
    22.         playership = GameObject.FindObjectOfType<ShipController>();
    23.         Vector3 startPosition = playership.transform.position + new Vector3 (0, 0.6f, 0);
    24.         GameObject laser = Instantiate(laser1, startPosition, Quaternion.identity) as GameObject;
    25.         laser.GetComponent<Rigidbody2D>().velocity = new Vector3(0,projectilespeed,0);
    26.         AudioSource.PlayClipAtPoint(laserShot, transform.position);
    27.     }
    28. }
    29.  
    30.  
    31. public class Laser2 : MonoBehaviour, IWeapon
    32. {
    33.    
    34.     private ShipController playership;
    35.     public GameObject laser;
    36.     public float projectilespeed;
    37.     public AudioClip laserShot;
    38.    
    39.     public void Fire()
    40.     {
    41.  
    42.         Vector3 startPosition1 = transform.position + new Vector3 (-0.3f, 0.6f, 0);
    43.         GameObject laser1 = Instantiate(laser, startPosition1, Quaternion.identity) as GameObject;
    44.         laser1.GetComponent<Rigidbody2D>().velocity = new Vector3(0,projectilespeed,0);
    45.         AudioSource.PlayClipAtPoint(laserShot, transform.position);
    46.         Vector3 startPosition2 = transform.position + new Vector3 (0.3f, 0.6f, 0);
    47.         GameObject laser2 = Instantiate(laser, startPosition2, Quaternion.identity) as GameObject;
    48.         laser2.GetComponent<Rigidbody2D>().velocity = new Vector3(0,projectilespeed,0);
    49.         AudioSource.PlayClipAtPoint(laserShot, transform.position);
    50.  
    51.     }
    52. }
    53.  
    I have to create the variables inside each class right? or can I do that in just one place for the variables that they all share like the ship or the audio file?
     
  6. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Personally, rather than using an interface in this case, I'd create a generic weapon class. It depends on the game, of course, but for something like a space invaders clone, I doubt your weapons will vary enough to warrant the use of interfaces. One configurable weapon class will probably take care of most of them, and make the relevent methods virtual so you can override them if need be.

    For example: (These are very very quick examples to give you an idea. Your own code should be more thought out and better formatted)

    Code (CSharp):
    1. //Wee, a weapon class! It has options do describe many different weapon types.
    2. public class Weapon : MonoBehaviour {
    3.  
    4.     //All of the weapons in this example game fire a projectile. We'll have a link to a projectile prefab in each
    5.     //weapons prefab.
    6.     [SerializeField] protected GameObject projectilePrefab;
    7.  
    8.     //If automatic, our example weapons will keep firing until you let go
    9.     [SerializeField] bool automatic;
    10.  
    11.     //The minimum delay between weapon shots.
    12.     [SerializeField, Range(0.1f, 4f)] float cooldown;
    13.  
    14.     float fired_timestamp = 0f;
    15.  
    16.     public bool trigger_pulled = false;
    17.  
    18.     protected virtual void Fire()
    19.     {
    20.         Instantiate(projectilePrefab, transform.position, transform.rotation);
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         if (Time.time < fired_timestamp + cooldown || !trigger_pulled)
    26.             return;
    27.                    
    28.         if (!automatic)
    29.             trigger_pulled = false;
    30.        
    31.         fired_timestamp = Time.time;
    32.    
    33.         Fire();
    34.    
    35.     }
    36.  
    37. }
    38.  
    39. //Very quick projectile class. It can deal a preset amount of damage and it moves at a preset speed.
    40. //You'd have to add something to ensure it doesn't hit your player when you launch it.
    41. [RequireComponent(Rigidbody2D)]
    42. [RequireComponent(Collider2D)]
    43. public class Projectile : MonoBehaviour {
    44.  
    45.     [SerializeField, Range(1,100)] int damage;
    46.     [SerializeField] Vector2 velocity;
    47.  
    48.     protected virtual void OnCollisionEnter2D(Collider2D other)
    49.     {
    50.         //For example.
    51.         var ship = other.GetComponent<SpaceShip>();
    52.         if (ship)
    53.             ship.Damage(damage);
    54.        
    55.         Destroy(gameObject);
    56.     }
    57.  
    58.     void Start()
    59.     {
    60.         GetComponent<Rigidbody2D>().velocity = velocity;
    61.     }
    62.  
    63. }
    Even as is, these two classes could describe an array of different weapons, assuming you plug in different values and use different art. A fast repeater machine gun, or a slow firing, heavy hitting canon.

    Further more, you can extend the classes for more functionality:
    Code (CSharp):
    1. public class DoubleWeapon : Weapon {
    2.  
    3.     //Our double weapon fires two projectiles instead of one.
    4.     protected override void Fire()
    5.     {
    6.         Instantiate(projectilePrefab, transform.position, transform.rotation);
    7.         Instantiate(projectilePrefab, transform.position + transform.right, transform.rotation);
    8.     }
    9. }
     
  7. saberwolfcdw

    saberwolfcdw

    Joined:
    Sep 21, 2015
    Posts:
    14
    Thanks for the help. Unfortunately, I am needing the weapon to fire on its own (no button pressed or held down) and just change what weapon is firing according to a variable that changes (Most likely with a PowerUp). I have it working at the moment already with space pressed and held to fire and 1-3 to select different weapons.
     
  8. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Well, if you read through the code you'll find that's a simple change. Just remove the 'automatic' and 'trigger_pulled' variables. Update will do the rest.
     
  9. saberwolfcdw

    saberwolfcdw

    Joined:
    Sep 21, 2015
    Posts:
    14
    My apologies. I am new to coding and this stuff confuses me still. I wasn't trying to offend. In your code that you posted, is that one weapon per script or multiple weapons per script?