Search Unity

Substitute for Raycasts/Less Raycasts?

Discussion in 'Scripting' started by teatime, Aug 31, 2009.

  1. teatime

    teatime

    Joined:
    Jun 16, 2008
    Posts:
    129
    In my game, the player character points a flashlight at whatever surface is behind the cursor at any given time. I chose this control setup because it makes sense (you only ever want to point a flashlight *at* something, not into empty space, right?) and it allowed me the most freedom with camera angles: I can have zoomed-out top-down scenes or scenes where the camera is level with the character and it works the same in both. However, when moving the cursor slowly from one object to another that is very different in distance, when the edge is crossed the player character changes his angle of focus in a way that is technically logical but fussy and unintuitive from a gameplay standpoint. Understand that this is not an issue Lerping would fix; the player character's arm movements are already plenty smoothed. The problem is that the flashlight's target is determined by only a single tiny point in space and it's dragging a big spotlight along with it (as opposed to say, the muzzle of a gun, or something else that a single raycast from a crosshair would be fine for.) I'm not trying to smooth time, I'm trying to smooth space, if that makes any sense. I thought casting several rays around the cursor and averaging the results would fix this:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CursorHit : MonoBehaviour {
    5.    
    6.     public HeadLookController headLook;
    7.    
    8.     Vector3 hitPointCenter;
    9.     Vector3 hitPointUpLeft;
    10.     Vector3 hitPointUpRight;
    11.     Vector3 hitPointDownLeft;
    12.     Vector3 hitPointDownRight;
    13.     Vector3 hitPointUp;
    14.     Vector3 hitPointRight;
    15.     Vector3 hitPointDown;
    16.     Vector3 hitPointLeft;
    17.        
    18.     public void CursorUpdate () {  
    19.         int layerMask = ~((1 << 8) | (1 << 2)); ; //touch everything but player and ignore raycast layer
    20.        
    21.        
    22.         Ray cursorRayCenter = Camera.main.ScreenPointToRay(Input.mousePosition);
    23.         RaycastHit hit1;
    24.         if (Physics.Raycast(cursorRayCenter, out hit1, Mathf.Infinity, layerMask)) {
    25.             hitPointCenter = hit1.point;
    26.         }
    27.        
    28.         Ray cursorRayUpLeft = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(-10,10,0));
    29.         RaycastHit hit2;
    30.         if (Physics.Raycast(cursorRayUpLeft, out hit2, Mathf.Infinity, layerMask)) {
    31.             hitPointUpLeft = hit2.point;
    32.         }
    33.        
    34.         Ray cursorRayUpRight = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(10,10,0));
    35.         RaycastHit hit3;
    36.         if (Physics.Raycast(cursorRayUpRight, out hit3, Mathf.Infinity, layerMask)) {
    37.             hitPointUpRight = hit3.point;
    38.         }
    39.        
    40.         Ray cursorRayDownLeft = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(-10,-10,0));
    41.         RaycastHit hit4;
    42.         if (Physics.Raycast(cursorRayDownLeft, out hit4, Mathf.Infinity, layerMask)) {
    43.             hitPointDownLeft = hit4.point;
    44.         }
    45.        
    46.         Ray cursorRayDownRight = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(10,-10,0));
    47.         RaycastHit hit5;
    48.         if (Physics.Raycast(cursorRayDownRight, out hit5, Mathf.Infinity, layerMask)) {
    49.             hitPointDownRight = hit5.point;
    50.         }
    51.        
    52.         Ray cursorRayUp = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(0,20,0));
    53.         RaycastHit hit6;
    54.         if (Physics.Raycast(cursorRayUp, out hit6, Mathf.Infinity, layerMask)) {
    55.             hitPointUp = hit6.point;
    56.         }
    57.        
    58.         Ray cursorRayRight = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(20,0,0));
    59.         RaycastHit hit7;
    60.         if (Physics.Raycast(cursorRayRight, out hit7, Mathf.Infinity, layerMask)) {
    61.             hitPointRight = hit7.point;
    62.         }
    63.        
    64.         Ray cursorRayDown = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(0,-20,0));
    65.         RaycastHit hit8;
    66.         if (Physics.Raycast(cursorRayDown, out hit8, Mathf.Infinity, layerMask)) {
    67.             hitPointDown = hit8.point;
    68.         }
    69.        
    70.         Ray cursorRayLeft = Camera.main.ScreenPointToRay(Input.mousePosition + new Vector3(-20,0,0));
    71.         RaycastHit hit9;
    72.         if (Physics.Raycast(cursorRayLeft, out hit9, Mathf.Infinity, layerMask)) {
    73.             hitPointLeft = hit9.point;
    74.         }
    75.        
    76.         transform.position = (hitPointCenter + hitPointUpLeft + hitPointUpRight + hitPointDownLeft + hitPointDownRight + hitPointUp + hitPointRight + hitPointDown + hitPointLeft) * 0.11f;
    77.        
    78.         headLook.target = transform.position;
    79.     }
    80. }
    81.  
    And indeed it does help, but it's not quite perfect. One problem is that spreading the raycasts apart by pixels will yield inconsistent results at different distances and resolutions, but that's not the biggest issue right now. The worst matter is that this is still not enough. The cursor still changes too oddly for objects that are very different distances, and if the cursor is moved very very slowly or if it is held stationary on an object that is moving in and out of its range you can tell when the edge raycasts are falling off the object. And while in a perfect world I could just cast 100 rays all around, average them, and call it a day, I'm already seeing a not-catastrophic but noticeable FPS drop from doing this many. Is there any way I can make this smoother without increasing the number of raycasts?
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    When you used a single ray, were you using the RaycastHit's transform value to identify the target transform and then point the flashlight at the transform.position? If you do this, you will find that the target position stays fixed until the cursor finds something else - the target will then jump abruptly to the new object and then be fixed on that instead. If you focus on the RaycastHit's point value instead (ie, the point in space where the hit occurs) you should find the light moves more smoothly. However, if the game is third-person and the camera doesn't follow directly behind the player character, then you may find it more difficult to get the right behaviour because the line-of-sight of the player and that of the camera don't match. In this case, if the player attempts to acquire the same target point that the camera does then it doesn't necessarily look convincing since their points of view could be very different.