Spherecast ARghhh

HI everyone

I am attempting to add power ups to my game by adding the power ups after a certain amount of time.
Now obviously i dont want the power to appear in geometry or off the level and the idea is to have the powerup to appear slightly offset from the player so it pops off camera.

I am attempting to use Sphere casts to ensure the item will be placed in a good position but sadly, i am struggling.

Can anyone give me a nudge if what i am attempting is in the correct direction or completely off?
Thank a you all. Code below:

  void loadUpgrade()

   {
     //Loads in health Upgrade within a certain distance of player. Ideally just off screen.

     Vector3 offset =  new Vector3  (1f ,9f, 1f);
     Vector3 shotAttempt = player.position + offset;

     shootRay.origin = shotAttempt;
//transform. forward is in there as the code was erroring without it. But i dont think it should be there.
     if (Physics.SphereCast (shotAttempt, 2.5f,transform.forward , out Shothit, 10f, shootableMask)) {
       Debug.Log ("Raygun");
     } else {
       Debug.Log ("Miss");
     }


   }

If you need random position around your player, you could use:

public Transform source;      //<----  assign player here
public float radius = 5f;

Vector3 spawnPosition = source.position + Random.onUnitSphere * radius;

EDIT:
Ah, sorry I misunderstood what you want to achieve. Ignore my post x_x

EDIT 2:
Is the game 3D or 2D?

Im adapting the survival shooter tutorial. So its a kind of top down 3d.

I think what you offered would work but i need to make sure it doesn’t appear inside geometry of the level.

As a temporary solution, you could use that above code to generate a random spawning point and use the snippet in the provided link:

As a powerUpSize set a value equal to the size of power up. If the result is Length > 0, repeat the process. Something like a brute force, iterate like 1000 times:

for (int i = 0; i < 1000; i++)
{
     Vector3 spawnPosition = source.position + Random.onUnitSphere * radius;

     float powerUpSize = 1f;
     GameObject[] objs = FindGameObjectsInsideRange (spawnPosition, powerUpSize);
     if (objs.Length == 0)
     {
          SpawnPowerUp(spawnPosition);
          break;
     }
}

If it fails 1000 times, most probably there is no room for power up. Of course, you can tweak that number if performance is an issue.

That is, this solution could work until you create something better in my opinion.

You are calling SphereCast with a distance of 10. This is checking if there are any objects within the sphere as it moves “10 distance” in the “forward direction” from your spawn point. You could try with a distance of 0, though I have no experience with how that might behave.

ok ive made a little progress with this now.

The ray cast sphere is working. Horah. Issue i am having is down to where i instantiate the Upgrade.
In theory i beleive what i have written should make the raycast fire to an offset of my player and then the object should spawn where the raycast hit. However it seems to always appear where the player originally started in the game.

Im assuming this is something simple im missing, but just cant see it.
Any help?

  void loadUpgrade()

   {
     //Loads in health Upgrade within a certain distance of player. Ideally just off screen.

     Vector3 offset =  new Vector3  (10f ,5f, 10f);
     Vector3 shotAttempt = player.position + offset;

     shootRay.origin = shotAttempt;
     Debug.Log ("Fire");
     //Debug.DrawRay ;
     if (Physics.SphereCast (shotAttempt, 0.5f,Vector3.down  , out Shothit, 10f, shootableMask)) {
       Instantiate (healthUpgrade,Shothit.transform.position +  new Vector3(0f,1f,0f),  Quaternion.Euler(0, 0, 0) );
       activeUpgrades = 1;
     } else {
       Debug.Log ("Miss");
     }


   }

Check the documentation on RayCastHit. You’ll see that the Shothit.transform.position property that you are accessing is the “the transform of the rigidbody or collider that was hit.” Since your raycast is going down you are hitting the ground, which by default has a transform of 0, 0, 0. Which is why your power up is appearing at the origin.

Most likely, you want Shothit.transform.point.

That’s all it was. Thank you :smile:

Now i need to figure out why the upgrade is appearing geometry when it should only appear where the raycast hits the floor.

Am i right in thinking if an object is defined on a different layer the raycast will not see it?

Not necessarily. You need to set up a Layering Filter and pass it as the sixth parameter to SphereCast. I see you are sending something called shootableMask but you don’t show the declaration or assignment.

I’m also confused about why you are shooting a SphereCast downwards at all. Your original post and the comments in your code both suggest you want to accomplish something different…

Problem I have is this is the first time I’ve used a sphere cast. I was finding without the vector 3 down the code was erroring so assumed it was meant to be there.
After inputing the vector down the raycast started hitting my shootablemask which is actually the floor layer it is referencing.

However I am finding the item appearing inside objects which I was hoping a sphere cast would stop happening.

A SphereCast is very similar to a RayCast, so I think that is a good place to start.

Think of a RayCast as a laser pointer. It is good for checking if there are objects in the way of travel. It is not intended to check if there is something at the starting or the final position.

It might be possible to use RayCast/SphereCast for your purpose but it is probably not the best choice. It is best used for checking if something can travel to a destination without hitting a wall, or checking is something is visible etc… This is why RayCast and SphereCast both take a direction and distance as parameters but they don’t really make sense for your purpose which is why you are getting confused.

I think you should consider using Physics.OverlapSphere instead. This simply checks if there is any colliders within a spherical area without trying to move around.

Right I see.
That’s why i set a y offset so the raycast started up in the air and shot below where the floor plane is with a small radius to cover the size of the power up. This was in the hope that if the ray didn’t hit the floor it wouldn’t spawn the item.

Is the way I have worked it def not the way to go then?

Well, it’s still possible your approach will work but I’m not sure why you think hitting the floor is important. Won’t you always hit the floor? Unless you accidentally choose a location outside the range of your map…

Without seeing your scene, it is difficult to determine if it’s possible to miss hitting the floor. It’s possible that your scene has a big, rectangular pane with additional geometry on top. Or perhaps you created a floor that was exactly the shape you needed and there is nothing to hit behind the walls…

Further, SphereCast will return true even if the location is partially behind a wall. As the SphereCast moves downwards, it should detect that part of the sphere collides with the ground. I think there is a logic mistake in assuming that “because a sphere would touch the ground if it fell from this location, a real sphere must fit here”.

Maybe you thought SphereCast would only return true if the entire sphere collided with the ground. That’s not the purpose of RayCast/SphereCast though. These functions are supposed to determine if any part of the ray / sphere would collide while moving in the given direction for the given distance.

OverlapSphere is more semantically correct, so I think it will cause you less confusion, that’s all.

Yeah my scene has a plane with geometry on top of it.

Your are correct in my. Understanding was the full sphere would have to collide with the floor plane.
I tried to counteract this with making the sphere cast radius small, but I was finding the item still spawning inside items way bigger than the sphere cast.
I will adapt the code to use overlap sphere as suggested . See if I have any better luck.