0
\$\begingroup\$

I'm trying to create reusable scripts that I can drop on my GameObjects. I am currently trying to figure out how to build a projectile script. Ideally, I want to be able drop this scrip on an enemy, give it a gameobject prefab to shoot (bullet, stone, arrow, energy ball, newspaper, whatever) and it will spawn that object and make it move.

Right now, the object is just Instantiated at the enemy's position.

Then enemies can walk around (top down 2d), and face one of the 4 directions (up, down, left, right). That part works fine.

How can I get the parent game object (an enemy) current facing direction so I can make the projectile travel that direction? I have a bool canShoot that I want to be able to check if it is true and then use that to fire the projectile. Maybe not all of the same time of enemy shoots something.

I made and accessor to the enemyMovement variable. But I get a 'this is ambiguous error.

If someone can help me figure out how to either the facing direction or the bool, I should be able to figure out how to access the other variables.

Here is my walking controller script that is also attached to the enemy.

WalkingController Script

using UnityEngine;
using System.Collections;

public class WalkingController : MonoBehaviour {

    public float moveSpeed      = 0.75f;
    public float shootDamage    = 1.0f;
    public bool canShoot        = false;    

    public Animator     anim;
    public Rigidbody2D body;

    public Vector2 enemyMovement;


    void Start () {
        anim = GetComponent<Animator> ();
        body = GetComponent <Rigidbody2D> ();
        StartCoroutine ("ChangeDirection");
    }

    void FixedUpdate () {
        body.MovePosition (body.position + enemyMovement * moveSpeed * Time.deltaTime);
    }
    public Vector2 getEnemyMovement() {
    return enemyMovement;
}
    IEnumerator ChangeDirection() {
        Debug.Log ("Change Direction");
        while(true){
            yield return new WaitForSeconds(Random.Range (0.3f, 1.5f));
            randomDirection();
        }
    }

    void randomDirection() {
        switch(Random.Range (0,3)){
            case 0:
                //Debug.Log ("Moving enemy Up");
                 enemyMovement = new Vector2 (0,1);
                break;
            case 1:
                //Debug.Log ("Moving enemy Right");
                 enemyMovement = new Vector2 (1,0);
                break;
            case 2: 
                //Debug.Log ("Moving enemy Down");
                 enemyMovement = new Vector2 (0,-1);
                break;
            case 3:
                //Debug.Log ("Moving enemy Left");
                 enemyMovement = new Vector2 (-1,0);
                break;
            default:
                 enemyMovement = new Vector2 (0,0);
                break;
        }
        anim.SetBool ("Walking", true);
        DefineFacingDirection (enemyMovement);

    }

    void DefineFacingDirection (Vector2 vec){
        anim.SetFloat ("InputX", vec.x);
        anim.SetFloat ("InputY", vec.y);
    }

}

Projectile Script

using UnityEngine;
using System.Collections;

public class Projectile : MonoBehaviour {

    WalkingController wcs; // THIS DIDN"T WORK EITHER
    public GameObject objectToSpawn;
    /*public GameObject parentObject;*/
    public float timeToSpawn = 1.0f;
    public int maxObjects = 1;
    public float maxSpeed = 1.1f;
    public bool ShootAtPlayer;

    private float internalTimer = 0.0f;
    private Rigidbody2D body;

    void Start() {
        //parentObject = gameObject.GetComponent<WalkingController> ();
        body = gameObject.GetComponent <Rigidbody2D>();
 wcs = GetComponentInParent <WalkingController> ();
    }

    // Update is called once per frame
    void Update () {
        this.internalTimer += Time.deltaTime;

        if (this.internalTimer > this.timeToSpawn) {
            this.Spawn();
            this.internalTimer = 0.0f;
        }
    }

    private void Spawn()
    {

//Vector2 enemyMovement = new Vector2(GetComponentInParent <WalkingController> ().enemyMovement.x, GetComponentInParent <WalkingController> ().enemyMovement.y) ;

        Instantiate(this.objectToSpawn, this.transform.position, Quaternion.identity);


 //this doens't work. call is ambiguous????
        this.body.MovePosition (this.objectToSpawn.transform + wcs.enemyMovement() * maxSpeed * Time.deltaTime);

        Destroy (this.objectToSpawn, 4.0f);
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ What if you try this.objectToSpawn.transform.position instead of just this.objectToSpawn.transform? Which of the calls is ambiguous(try simplifying the expression step by step)? Also are you sure this(=Monobehaviour) has body - according to the documentation there is none. \$\endgroup\$
    – wondra
    Feb 27, 2016 at 16:46

1 Answer 1

2
\$\begingroup\$

I'd be tempted to flip this around, and put the Projectile script on the prefab that gets fired (ie. the bullet/missile/fireball/what-have-you)

// Since we want this to fly with velocity & detect collisions, 
// we'll trust the Rigidbody2D component to handle these aspects.
[RequireComponent(typeof(Rigidbody2D))] 
public class Projectile : MonoBehaviour {

    // Projectile firing speed - might want damage/explosion fields too...
    public float speed;

    // Cache a reference to the body so we don't look it up every time.
    Rigidbody2D _body;

    // Initialize the Rigidbody2D reference so it's ready when we need it.
    void Awake() {
        _body = GetComponent<Rigidbody2D>();
    }

    // Expose method to fire a projectile.
    public Projectile Shoot(Vector3 origin, Vector3 direction) {

        // This gets called on the prefab, which never gets an Awake,
        // so we need to initialize this bit ourselves.
        if(_body == null)
            _body = GetComponent<Rigidbody2D>();

        // Spawn a copy of this GameObject, and get a reference to the
        // spawned copy's Projectile script instance.
        Projectile bullet = (Projectile)Instantiate(
                              this,
                              origin,
                              Quaternion.LookRotation(direction));

        // Awake() runs on our spawned bullet, so its _body property is set.
        // Fire the bullet the way we want it to go.
        bullet._body.velocity = direction * speed;

        // Return a reference to it, in case our parent script 
        // wants to do something with it.
        return bullet;
    }

    void OnCollisionEnter2D(Collision2D collision) {
        // Implement "projectile hit" logic here or in OnTriggerEnter2D
    }
}

Now if I want to build an enemy or turret that uses this, I just need two lines...

public Projectile projectilePrefab;

...to expose an Inspector field to assign the projectile prefab into. And when I want to fire....

projectilePrefab.Shoot(barrelTransform, firingDirection.normalized);

This decouples the Projectile script from your WalkingController - the bullet doesn't have to care who fired it or what kind of logic they want to use to do so (maybe some characters fire on a timer, others in bursts, others only when they see an enemy...) - its job is just to spawn & shoot when they tell it to, and damage whatever it hits.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .