Minecraft style block placement

hello everybody, i am trying to make kind of a minecraft like block placment mecanic, i have the block placement half done, the main problem i have now is the fact that i have no idea on how to snap the block into a possition. if someone has any idea on ow to make this happen pls let me know. note that i am certainly not asking for code, simply an idea on how can this be done. thanks a lot for your help.

I found this tutorial really useful. It starts with a Minecraft like system, but takes it much further later on.

This is a simple way I came up with:

As you may have guessed, the idea is to cast a ray in the player camera’s forward direction, and we are interested in the point of collision of this ray.

Destroying a block is simple, we just identify the Collider’s gameObject, and destroy it, like this:

if (Input.GetKeyDown(KeyCode.Mouse0))  // left click --> destroy block
{
            RaycastHit hitInfo;
            if (Physics.Raycast(playerCam.transform.position, playerCam.transform.forward, out hitInfo, range, playerMask))
            {
                Destroy(hitInfo.collider.gameObject);
            }
}

That’s really all there is to it.

Now, to place a block, it’s slightly trickier, but not that bad: We have to find which face of the block the
player is aiming at. For that, We cast a ray, identify the collider’s
gameObject, specifically its position (we call P) in world space (the idea is that the block’s
reference frame will lie right in the center of the block). Position P will have components x, y and z.
The key here is that P lies on the face of the block (obviously), but this means one of the components
of P finishes with .5. because in my case, the block has side length 1 unit.
We look at the relative position between point P and the position of the center of the block. One of its comonents will be -0.5f or 0.5f, and we just need to check will one! We write a method like this:

Vector3 SnappedPosition(Vector3 pointToSnap, Vector3 blockCenterPosition)
{
    Vector3 relativePosition = pointToSnap - blockCenterPosition;
  
    if (relativePosition.x == 0.5f) return blockCenterPosition + Vector3.right;
    else if (relativePosition.x == -0.5f) return blockCenterPosition + Vector3.left;
    else if (relativePosition.y == 0.5f) return blockCenterPosition + Vector3.up;
    else if (relativePosition.y == -0.5f) return blockCenterPosition + Vector3.down;
    else if (relativePosition.z == 0.5f) return blockCenterPosition + Vector3.forward;
    else if (relativePosition.z == -0.5f) return blockCenterPosition + Vector3.back;
    else return Vector3.zero;  // error (should not occur)
}

This will return the position the block’s center will lie at. Then we call this function after ray casting,
like so:

if (Input.GetKeyDown(KeyCode.Mouse1))  // right click --> place block
{
    RaycastHit hitInfo;
    if (Physics.Raycast(playerCam.transform.position, playerCam.transform.forward, out hitInfo, range))
    {
        Vector3 instantiatePosition = SnappedPosition(hitInfo.point, hitInfo.collider.transform.position);
        Instantiate(editingInfo.currentGameObject, instantiatePosition, Quaternion.identity);
    }
}

The complete code (which should be attached to the player Game Onject) will look like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
  
public class EditingTerrain : MonoBehaviour
{
  
    public Camera playerCam;
  
    public float range = 10f;
  
    public EditingInfo editingInfo;  // to retrieve the currently selected block from another script
  
    LayerMask playerMask;
  
  
    Vector3 SnappedPosition(Vector3 pointToSnap, Vector3 blockCenterPosition)
    {
        Vector3 relativePosition = pointToSnap - blockCenterPosition;
  
        if (relativePosition.x == 0.5f) return blockCenterPosition + Vector3.right;
        else if (relativePosition.x == -0.5f) return blockCenterPosition + Vector3.left;
        else if (relativePosition.y == 0.5f) return blockCenterPosition + Vector3.up;
        else if (relativePosition.y == -0.5f) return blockCenterPosition + Vector3.down;
        else if (relativePosition.z == 0.5f) return blockCenterPosition + Vector3.forward;
        else if (relativePosition.z == -0.5f) return blockCenterPosition + Vector3.back;
        else return Vector3.zero;  // error (should not occur)
    }
  
  
    void Start()
    {
        // to avoid collision with player camera during raycast
        playerMask = ~ LayerMask.GetMask("Player"); // everything except the player mask
    }
  
  
  
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Mouse0))  // left click --> destroy block
        {
            RaycastHit hitInfo;
            if (Physics.Raycast(playerCam.transform.position, playerCam.transform.forward, out hitInfo, range, playerMask))
            {
                Destroy(hitInfo.collider.gameObject);
            }
        }
  
        if (Input.GetKeyDown(KeyCode.Mouse1))  // right click --> place block
        {
            RaycastHit hitInfo;
            if (Physics.Raycast(playerCam.transform.position, playerCam.transform.forward, out hitInfo, range))
            {
                Vector3 instantiatePosition = SnappedPosition(hitInfo.point, hitInfo.collider.transform.position);
                Instantiate(editingInfo.currentGameObject, instantiatePosition, Quaternion.identity);
            }
        }       
    }
}

Hope that helps, even if I’m 6 years late.