Selective Raycasting

I’m completely new to Unity, coming over from Director. And the tutorials are mostly aimed at games, while I’m doing physics simulations (education). I’m trying to convert a fairly simple Director project to begin with. And I got stuck pretty early on. (I’m not that fluent in JS.)

Here’s the situation: I have a platform (table) and an object resting on that platform (puck). The user needs to be able to drag the puck around the table top, and drag the table to tilt it. Pretty straightforward, and I got both of these scripts working, with one exception. When I click to drag the table, it tilts as it should. But when I click to drag the puck, it moves as it should but the table also tilts.

The only potential problem I can see is that the puck is a child of the table. It needs to be so it moves correctly when the table is tilted. But I put the parts of the table that need to be clickable in one layer, and the puck parts in another layer. Could that be the problem? Does a hit on the child always include a hit on the parent?

Am I doing something wrong? Is there a way to get a list of objects the raycast hits? Any ideas?

I’m also confused by the rotation values. The maximum rotation for the table is supposed to be 20 degs, but I have to check for a maximum of 0.2 degs. How does that work?

//table code
function Update () {
    var step = 0.10;
    var xRotation = 0.0;
    var layerMask = 1 << 8;
    
    if (Input.GetMouseButton (0)) {        
    	var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    
        if (Physics.Raycast (ray.origin, ray.direction, Mathf.Infinity, layerMask)) { 

           	xRotation += Input.GetAxis("Mouse Y") * step;
    		transform.rotation.x += xRotation;
    		
       	 	if (transform.rotation.x > 0.200){
    			transform.rotation.x = 0.200;  
    		 }
    		if (transform.rotation.x < 0.0){
    			transform.rotation.x = 0.0;
    		 }
        }
    }
}

//puck code
function Update () {
    var step = -0.00120;
    var xPosition;
    var zPosition;
	var layerMask = 1 << 9;
    
    if (Input.GetMouseButton (0)) {        
    	var ray = Camera.main.ScreenPointToRay (Input.mousePosition);		

        if (Physics.Raycast (ray.origin, ray.direction, Mathf.Infinity, layerMask)) { 

           	xPosition = Input.GetAxis("Mouse X") * step;
           	zPosition = Input.GetAxis("Mouse Y") * step;
           	transform.Translate (xPosition, 0, zPosition);
 
       	 	if (transform.position.x > 0.0270){
    			transform.position.x = 0.0270;
       		 }
    		if (transform.position.x < -0.027){
    			transform.position.x = -0.027;
    		 }
       	 	if (transform.position.z > 0.0270){
    			transform.position.z = 0.0270;
       		 }
    		if (transform.position.z < -0.027){
    			transform.position.z = -0.027;
    		 }
       	}
    }
}

Hi!

The reason why both the table and puck are rotated when you drag the puck is simple: you do two raycasts, one in each update, one of them hits the table, the other the puck, so both get modified.

I would use only one raycast instead - preferably not in the objects’ update method (remember to include both layers), and then apply the necessary changes on the object that was actually hit. You can get that info from the RaycastHit parameter of Physics.Raycast Then I’d define an onDrag() method for both the table and the puck, and use GameObject.SendMessage to activate it.

Transform.rotation is a Quaternion, not Euler angles, so it’s components are not in degrees. I would advise against modifying it’s components directly unless you know what you’re doing. You can use Transform.eulerAngles to access the rotation directly as Euler angles, however, read the documentation page thoroughly to make sure you know the potential pitfalls.

Thanks. Makes sense. I’m still trying to figure out where to put code in Unity.

A few questions:

I get that there are two raycasts done, but why are both objects not affected? Dragging the puck affects the table and puck, but dragging the table only affects the table. Doesn’t specifying a layerMask determine which object gets a hit?

If not in the object’s update method, then where else can I put the code? Where does the raycast code and the OnDrag method go?

I also couldn’t figure out how to get the object that was hit from RayCastHit. The Collider seems to just return the generic type of collier. How is the hit object uniquely identified in the RayCastHit parameters?

Sorry for the very basic questions, but I have not been able to find any sample code for this sort of thing. It would be really helpful if I could see how directly manipulating objects with the mouse is typically done.

Let’s break down what happens in each case:

Dragging the table:

  • engine calls the Update in table
  • raycast against the table layer hits table
  • table dragging code gets executed
  • engine calls the Update in puck
  • raycast against the puck layer hits nothing
  • puck dragging code does not get executed

Dragging the puck:

  • engine calls the Update in table
  • raycast against the table layer hits table
  • table dragging code gets executed
  • engine calls the Update in puck
  • raycast against the puck layer hits puck
  • puck dragging code gets executed

I’ve highlighted the difference for you. Note that there is no ‘magic’ going on behind the scenes: when you click the puck and raycast at that position while ignoring the puck, you will hit the table :slight_smile:

I would create an empty GameObject and put it in there in a script. The reason why I don’t like having it in the objects’ update is because dragging them is really an external influence. They should be able to react to it, but not actually perform it. Note that this is my way of thinking and it’s not a golden rule. But think about this example: if you wanted to add further objects that are draggable, this way all you have to do is implement an OnDrag method for them. Pretty neat huh?

The generic collider is all you need:

// perform the raycast here
GameObject hitObject = raycastHit.collider.gameObject;
hitObject.SendMessage("OnDrag", null, SendMessageOptions.DontRequireReceiver);

No problem, always happy to help. Have a look around the wiki, maybe you’ll find something along these lines. Just keep in mind no solution is a silver bullet and different situations call for different approaches.