Hello everyone,
I’m having some troubles to use the raycast function.
I have a terrain with a layer called “Ground”. On a player movement script, I have a Physic.Raycast call with a layer mask “Ground”.
So when I click on the ground, no problem, the hit is detected and my character move.
I have an other script to collect items. But when I click on a object, like a cube, the hit is also detected the ground.
Is there a way to stop the click on the cube (the first gameobject)?
I have a script for movement and an other script to collect item, this is why I don’t want to do a if(){} else {} with all type of item in my game.
So, the Terrain GameObject has “Ground” layer and the item (a cube) has the “InteractItem” layer
MovementScript → Update() :
if (Input.GetButton("Fire1"))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (!EventSystem.current.IsPointerOverGameObject())
{
if (Physics.Raycast(ray, out hit, 100, LayerMask.GetMask("Ground"), QueryTriggerInteraction.Ignore))
{
Debug.Log("Hit the ground");
//Code to move the player
}
}
}
InteractScript → Update() :
if (Input.GetButtonDown("Fire1"))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (!EventSystem.current.IsPointerOverGameObject())
{
if (Physics.Raycast(ray, out hit, 100, LayerMask.GetMask("InteractItem"), QueryTriggerInteraction.Ignore))
{
Debug.Log("Hit an item");
//Code to pick the item
}
}
}
When I click on the cube, the player is moving, I just want him to pick the item without moving.
Is second script attached to every pickable GameObject?
You would be better to have dealing with raycast in single script and collect all collisions of pickable objects.
Anyway, what I suggest however, execute picking object raycast first, then add logic, if not picked, then check ground collision.
With single Ray / script is much easier to control such behaviour.
The interact script is on the player (he’s the one who interact).
For the single script, I understand it’s maybe easier in one script but I think it’s going to be really difficult to maintain in a real game (with a lot of different element type/actions …).
But in case i’ll go for a single script, you say to do something like that ?
f (Input.GetButtonDown("Fire1"))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (!EventSystem.current.IsPointerOverGameObject())
{
if (Physics.Raycast(ray, out hit, 100, LayerMask.GetMask("???"), QueryTriggerInteraction.Ignore))
{
if(hit.collider.GetComponent<InteractItem>() != null)
// Code for interact
else if (hit.collider.GetComponent<Enemy>() != null)
// Code for attack
else if (hit.collider.GetComponent<XXXX>() != null)
// Code for level transition
else if (hit.collider.GetComponent<XXXX>() != null)
// Code for character talk
else if (hit.collider.GetComponent<XXXX>() != null)
// Code for moving
else if ...
}
}
}
This is the kind of thing I want to avoid
And with this solution, I’ll still have a problem because I can’t use the QueryTriggerInteraction.Ignore without giving a layer mask (i probably can give all existing masks i guess).
Then you can use foreach / for loop and you can use switch case.
You can use dictionary, with key and value <Collider, MyType>, then in condition call a method, with desired function.
This approach allows you, to see all types in one script. It may be potentially easier to debug as well.
Otherwise, you need know somehow, when you collide with something else, before checking with ground.
You may want override some bool status value. Only need make sure, you set, read and reset status in right times.
With multiple calls form different GameObjects, when attaching scripts to them, yet having ground check in current approach, it may become ugly quite quick.
There may be different solutions as well. This is just quick idea, out of top of my head.
I don’t know why the raycast doesn’t work as expected for you, but concerning the implementations I would also recommend to have the raycast in just one component.
to trigger different kinds of interactions, you could have a base class with an Interact() method that all other behaviors that can be clicked (enemies, the ground, items and so on) need to implement. You can then just ask GetComponent for the base class and execute no matter what implantation is used on the actual object.
also: try the EventSystem and a PhysicsRaycaster. It’s a very powerful system that really decouples a lot for you.