Search Unity

making in game block placement exactly like how the dragger tool works in Roblox

Discussion in 'Scripting' started by Glowball123, Dec 6, 2020.

  1. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    im developing a game that involves you building a boat that has physics applied to it. Im trying to figure out how to make a building tool that works the same as the dragger in roblox. so far i have a character that can move around, and a raycast that is shot every update in the position and direction of my camera. A cube is sent to the position of the raycast hit every update, and also has its rotation set to the rotation of the game object hit by the raycast. The part that im stuck with however is this: I want my cube to snap to a grid, relative to the position and rotation of the game object hit by the raycast. How would i go about doing this?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    If you have a continuous coordinate coming in (I'll just talk about one axis, say "X" axis), and you want to quantify it into discrete snaps, here's the general approach:

    1. divide by the grid distance
    2. cast it to an integer (or use Mathf.Round())
    3. multiply that integer back by the grid distance.

    Now to get the grid aligned an arbitrary object, you need to transform the incoming coordinate to local object space, do the snapping, then de-transform it back to your original coordinate space.

    I believe these will be your magic sauces:

    https://docs.unity3d.com/2019.3/Documentation/ScriptReference/Transform-worldToLocalMatrix.html

    https://docs.unity3d.com/2019.3/Documentation/ScriptReference/Transform-localToWorldMatrix.html
     
  3. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    its kind of hard to wrap my head around all of this, specifically the part where i convert it to local space and then world space, can you give me an example?
     
  4. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78


    Code (CSharp):
    1. RaycastHit hit;
    2.         if (Physics.Raycast(pointer.position, pointer.forward, out hit, range))
    3.         {
    4.             Vector3 snapPos;
    5.             snapPos = hit.transform.worldToLocalMatrix * hit.point;
    6.             snapPos = new Vector3((int)snapPos.x,0.5f,(int)snapPos.z);
    7.             Vector3 finalpos = hit.transform.localToWorldMatrix * snapPos;
    8.             highLightgo.transform.position = finalpos;
    9.             highLightgo.transform.rotation = hit.transform.rotation;
    10.         }
    i tried to replicate your answer and it doesn't work, is their anything wrong with my code?
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    Your code is almost there... took me a moment but there was one key missing part:

    - at the start subtract the position of the object, and at the end, put it back in.

    This is necessary to get the snapping numerically correct in local space, so it snaps to local (0,0,0) as you expect.

    Here is a fully-functioning script:

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SnapToGrid : MonoBehaviour
    6. {
    7.     public GameObject RawMarker;
    8.     public GameObject SnappedMarker;
    9.  
    10.     Camera cam;
    11.  
    12.     void Start()
    13.     {
    14.         cam = Camera.main;
    15.     }
    16.  
    17.     // does two things: mouse to world (if it collides), world to snapped
    18.     void SnapCursorToObject( Vector3 pointerPosition, Vector3 snapInterval)
    19.     {
    20.         float range = 100;
    21.  
    22.         Ray ray = cam.ScreenPointToRay( pointerPosition);
    23.  
    24.         RaycastHit hit;
    25.  
    26.         if (Physics.Raycast(ray: ray, hitInfo: out hit, maxDistance: range))
    27.         {
    28.             RawMarker.SetActive(true);
    29.             SnappedMarker.SetActive( true);
    30.  
    31.             Vector3 worldRawPosition = hit.point;
    32.  
    33.             RawMarker.transform.position = worldRawPosition;
    34.             RawMarker.transform.rotation = hit.transform.rotation;
    35.  
    36.             worldRawPosition -= hit.transform.position;
    37.  
    38.             Vector3 localRawPosition = hit.transform.worldToLocalMatrix * worldRawPosition;
    39.      
    40.             // snap
    41.             Vector3 localSnappedPosition = new Vector3(
    42.                 Mathf.Round( localRawPosition.x / snapInterval.x) * snapInterval.x,
    43.                 Mathf.Round( localRawPosition.y / snapInterval.y) * snapInterval.y,
    44.                 Mathf.Round( localRawPosition.z / snapInterval.z) * snapInterval.z
    45.             );
    46.  
    47.             Vector3 worldSnappedPosition = hit.transform.localToWorldMatrix * localSnappedPosition;
    48.  
    49.             // HACK: disable y snapping for this test purpose
    50.             worldSnappedPosition.y = worldRawPosition.y;
    51.  
    52.             worldSnappedPosition += hit.transform.position;
    53.  
    54.             SnappedMarker.transform.position = worldSnappedPosition;
    55.             SnappedMarker.transform.rotation = hit.transform.rotation;
    56.         }
    57.         else
    58.         {
    59.             RawMarker.SetActive(false);
    60.             SnappedMarker.SetActive(false);
    61.         }
    62.     }
    63.  
    64.     void Update()
    65.     {
    66.         SnapCursorToObject( Input.mousePosition, new Vector3( 1.0f, 10.0f, 1.0f));
    67.     }
    68. }
    Enclosed is a test package that brings it all together, with two planes marked with grids in local (1,1,1) intervales, one plane rotated, one not, and floating markers to show raw vs snapped positions. Works perfectly.
     

    Attached Files:

  6. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    so far it works great! There is an issue however, it doesn't work on cubes, when i use it on a plane it works perfectly but on cubes it just freezes at the center.
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    If you use the default Unity cube, you probably had to scale it up, otherwise it only has one snap at the 1x1 spacing.

    Scaling up makes the transformed snaps be massive. Instead, scale up the cube but remove its collider, then put another collider on that is the same size as it.