Your code has several issues. However first address your apparent error. A code path is the path of execution that the processor can go through your method until it finishes / leaves the method. Any kind of branching will create two possible ways how the execution might continue. A method that has a return value must always return a value, not matter which code path you might go. A “for” or “foreach” loop has a condition when to terminate. However in both cases there is the possibility that the loop doesn’t run at all. You use the method “FindGameObjectsWithTag”. Imagine there is no object tagged “bad”. That means the method returns an empty array. This will completely skip your foreach loop. So your execution just skips the foreach loop and you reach the end of the method without returning anything.
Keep in mind that the compiler is pretty “dumb” because even in situations where it’s seems obvious to you that you will reach a return statement, from the compilers view that’s not guaranteed.
So like others have already mentioned you just have to follow all your possible cases and make sure there is always a return statement when the method finishes. the easiest solution is to add a return statement at the end of the method.
However your method has some logical flaws. inside your foreach loop you have this if statement:
if (target == null)
{
return false;
}
This can never be true. Because if target is null at that point it means your “enemy” variable is null. However if that’s the case you get a null reference exception at line 10 since you try to access the transform component of enemy. So if enemy is null you won’t get past this point.
Your overall logic seems to be backwards and pretty messed up. Your loop does never ever get to the second element in the array, no matter what happens. Inside your loop you always have a return statement. Next strange thing is your method name. You called it “ShootEnemy” but it doesn’t seem to shoot anything. All it does is checking if the first enemy returned by FindGameObjectsWithTag is within the fireing range. In this case it rotates slowly towards that enemy and returns true, otherwise it returns false. So your method name is misleading. Also do not put too much different functionality into one method.
My guess is you want to rotated towards the closest enemy in range. However there are generally two targetting modes. Either lock-on target until it’s dead or out of range or always pick the closest one. The second mode has a slight issue when you rotate slowly towards your target. Since the distance to the enemies can change from frame to frame your object might rapidly switch between two or more enemies. Since you rotate slowly you might never actually point at any of the enemies. For example in the old towerdefence game onslaught you can change the targetting mode of every turret with the key “T”. It provides the modes nearest, furthest, weakest, strongest, slowest and fastest. Apart from those modes you can also toggle lock-on mode on or off (key “L”).
All those things aside I would split the code into logical pices like that:
Transform FindClosestEnemy()
{
Transform closest = null;
float minDist = float.PositiveInfinity;
float rangeLimit =rangeOfFire * rangeOfFire ;
foreach (GameObject obj in GameObject.FindGameObjectsWithTag("bad"))
{
float dist = (obj.transform.position - transform.position).sqrMagnitude;
if (dist < minDist && dist < rangeLimit)
{
minDist = dist;
closest = obj.transform;
}
}
return closest;
}
void RotateTowards(Transform aTarget)
{
Vector3 dir = aTarget.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * 5f);
}
bool Shoot()
{
Transform target = FindClosestEnemy();
if (target != null)
{
RotateTowards(target);
// shoot here ?!
return true;
}
return false;
}
Note this will always pick the closest enemy that is in range. To implement lock-on-target mode you have to use an actual target variable inside your class.
Transform target = null;
bool Shoot()
{
if (target == null || Vector3.Distance(target.position, transform.position) >= rangeOfFire )
{
target = FindClosestEnemy();
}
if (target != null)
{
RotateTowards(target);
// shoot here ?!
return true;
}
return false;
}
This will only look for a new target when the old one has been destroyed or when it’s out of range.