Fire projectile, tracing a Raycast, enemy AI, 2D

'Aight, here’s the situation:

  • Enemy patrols, depending on direction, speed and whether it’s hit a wall or not.
  • If the player walks inside its range, it stops and STARES O_O…
  • It also does some damage and knocks the player back on collision - standard stuff…

What I want it to do:

  • Instead of STARING at the player … I want it to trace a raycast, instantiate my laser object and apply force in the direction ( or using the angle ) of the raycast.
  • Alternatively … I could make it shorter than the player and make it a dull enemy firing in a straight line … but that wouldn’t be fun now would it.

Here’s my script so far with the note that I already have some working enemies and a boss fight, it’s just this that I’m having an issue with, I don’t want to be spoon fed and would much rather theoretical responses with short examples THANKS!

public class Enemy1 : MonoBehaviour 
{
	//Public variables
	public float moveDistance; //Distance the enemy will move on both sides/aka Patrol distance
	public float moveSpeed; //Speed at which the enemy will move

	//Private variables
	private bool playerInside; //Used to check if the player is inside
	private float orgPos; //The enemy's original position
	private float useSpeed; //Some more speed variables
	private bool wallInTheWay; //Indicates if a wall has been hit
	private Animator _anim1; //Used for animation, specifically the shooting one

	//Some general logic 
	void LateUpdate()
	{
		Debug.DrawRay (transform.position,GameObject.Find ("nyan").transform.position - transform.position , Color.green);
	}

	//Setting up CoUpdate() and variables
	private IEnumerator Start()
	{
		//Grab the component so that we can animate stuff
		//_anim1.gameObject.GetComponent<Animator> ();

		//Patrol speed and initial position
		useSpeed = -moveSpeed;
		orgPos = this.gameObject.transform.position.x;

		//Assign bools to false, avoiding any stupid scenarios
		wallInTheWay = false;
		playerInside = false;

		//And finaly start CoUpdate()
		yield return StartCoroutine (CoUpdate ());
	}

	//CoUpdate - Update() with the block functionality
	private IEnumerator CoUpdate()
	{
		//Stuff here happens once

		//Update
		while (true) 
		{
			//Screem, aim and fire
			if(playerInside)
			{
				yield return StartCoroutine(ScrAimFire());
			}
			//Or... wait for it... waaaaait for it...
			else if(!playerInside)
			{
				//If there is no wall in the way patrol in regular intervals 
				if(!wallInTheWay)
				{
					//Based on position, assign direction 
					if(orgPos - this.transform.position.x > moveDistance)
						useSpeed = moveSpeed;
					else if(orgPos - this.transform.position.x < -moveDistance)
						useSpeed = -moveSpeed;

					//And move...
					this.transform.Translate (useSpeed * Time.deltaTime, 0, 0);
				}
				//If there is a wall there however...
				else if(wallInTheWay)
				{
					//Translate the object accordingly
					yield return StartCoroutine(WallMovement());
				}
			}

			//Always - to avoid infinit loops and crashes
			yield return null;
		}
	}

	//Check if the player is in range
	private void OnTriggerEnter2D(Collider2D plr)
	{
		if (plr.gameObject.tag == "Player") 
			playerInside = true;
	}

	//Make sure to disable the trigger if the player is no longer in range
	private void OnTriggerExit2D(Collider2D plr)
	{
		if (plr.gameObject.tag == "Player")
			playerInside = false;
	}

	//Various collision checks
	private void OnCollisionEnter2D(Collision2D obj)
	{
		//If it's a player...
		if (obj.gameObject.tag == "Player") 
		{
			//Knockback and stop the player ... kinda messy due to all the component fetching
			if(obj.gameObject.GetComponent<Rigidbody2D>().velocity.x < 0)
			{
				obj.gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(0,0);
				obj.gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector3(2000.0f,0.0f,0.0f));

			}
			else if(obj.gameObject.GetComponent<Rigidbody2D>().velocity.x > 0)
			{
				obj.gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(0,0);
				obj.gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector3(-2000.0f,0.0f,0.0f));
			}
			//Also remember to do some damage
			_Player.curHealth -= 50;
		}

		//If it's a wall, make sure the rest of the script knows this
		if (obj.gameObject.tag == "Wall") 
			wallInTheWay = true;

		if (obj.gameObject.tag == "Lava")
			Destroy (this.gameObject);
	}

	//Make sure you know when you're no longer touching the wall
	private void OnCollisionExit2D(Collision2D obj)
	{
		if (obj.gameObject.tag == "Wall")
			wallInTheWay = false;
	}

	//Wall Logic - simple
	private IEnumerator WallMovement()
	{
		//This should do for now ... Plan on making it float back up later on
		RaycastHit2D rightWall = Physics2D.Raycast(this.gameObject.transform.position, Vector2.right, 5.0f);

		//If you hit a wall on the right
		if (rightWall)
			this.transform.Translate (-moveDistance, 0.0f, 0.0f);

		//Otherwise...
		else 
			this.transform.Translate (moveDistance, 0.0f, 0.0f);

		//And make sure you dont teleport/run back to the wall
		orgPos = this.transform.position.x;

		//ALWAYS!!
		yield return null;
	}

	//The actual scream, aim and fire logic
	private IEnumerator ScrAimFire()
	{
		yield return null;
	}
}

I’m assuming “nyan” is your player. As with my previous comment, typically you would find it once in Start() rather than call it in the bowels of your code. Assuming the projectile is coming from ‘transform.position’, then all you need to do is:

Transform player = GameObject.Find ("nyan").transform;
Vector3 dir = player.position - transform.position;
projectile.rigidbody2D.AddForce(dir.normalzied * someForceFactor);

Often a projectile is Instantiated and moved on it forward. So you would have something like:

Transform player = GameObject.Find ("nyan").transform;
Vector3 dir = player.position - transform.position;
Vector3 look = Quaternion.LookRotation(dir);
GameObject go = Instantiate(projectilePrefab, transform.position, look) as GameObject;
go.rigidbody2D.AddForce(go.transform.forward * someForceFactor);