# Object placement with a raycast (a hard problem with 3d logic)

I want to place an object to a hit point without intersecting with object that was hit, how do I do it?

Off the top of my head, try adding a radius variable to the object that is hit and the object to be placed, then back up the ray direction the distance of the sum of their radii. Of course, if you want the placed object aligned with some ground or such you will have to do some tinkering.

``````using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Positioner {
public static Vector3 FixedPosition (GameObject GroundObject, RaycastHit hit){
Vector3 oldPosition = GroundObject.transform.position;
GroundObject.transform.position = hit.point;
Vector3[] ObjectVertices = GroundObject.GetComponent<MeshFilter>().mesh.vertices;
List<Vector3> ProjectedVertices = new List<Vector3>();
List<float> VerticeDistance = new List<float>();
foreach(Vector3 vertice in ObjectVertices){        // Projects every vertex point to a line from linePoint
Vector3 linePoint = Vector3.MoveTowards(hit.point, hit.normal, Vector3.Distance(GroundObject.collider.bounds.max, hit.point));
Vector3 ProjectedVertice = Math3d.ProjectPointOnLine(linePoint, (hit.point - linePoint).normalized, GroundObject.transform.TransformPoint(vertice));
}
Vector3 FarthestPoint = ProjectedVertices[VerticeDistance.IndexOf(VerticeDistance.Max())];
float IntersectionDepht = Vector3.Distance(hit.point, FarthestPoint);
GroundObject.transform.position = oldPosition;
Debug.DrawRay(FarthestPoint, Vector3.up, Color.green, 2, false);
Vector3 finalPosition = hit.point + (hit.normal.normalized * IntersectionDepht);
return finalPosition;
}
}
``````

I want to move the object the depth of intersection from the surface to avoid intersecting, the problem is the offset. It’s always too big and skaling the object makes it even bigger and I don’t understand why. The purpose of that loop is to calculate every distance from vertex to the point that should be in free space. After the loop it calculates the depth of intersection, and moves the object away from the surface. Class Math3d is in Unify wiki. The object can be rotated.

I have bumbed this for a week. If you can’t help me with my code, is there any other solutions?

You could get the render bounds of the object and workout the offset then use the surface normals for rotation

Screenshots of whats happening?

And what kind of surface & object is that?

I tested it with two cubes. The positioned object is floating, scaling seems to affect offset somehow. I can’t get any screenshots right now, you could put that script into your scripts folder and test it.

No, bounding box wouldn’t work with this.

Maybe i’ve totally missed something.
But couldn’t you use the pivot point (which represents the objects position) as the position to place the object? Scaling wouldn’t affect any height-offset then.
Just make sure (in your desired modelling software) that it’s aligned with the ‘ground’ of the object you’re trying to place and once you got the hitpoint, you can theoretically just spawn the object there.

Of course you still have to check several things regarding whether you really want to spawn an object there (maybe you’ve raycasted on a little hill/peak, too close to another object or whatsoever).

My script places the object to the hit point and then fixes the position Again, I tested this with only two objects, one as a surface, second as an object to place. Pivot point is usually the middle point, if I do as you say it would be half inside the surface tha was hit. Your idea about the pivot point could work for The Forest like game, but I want to use any kind of object on any kind of surface. Stupid idea. Sorry.

Anyway thanks for your attention, I waited for relly long time, I tried to make a good solution for this for about six months.

Why wouldn’t bound.extents.(x/z) not work? either renderer or collider bounds…

I don’t think it is good for complex shapes.

If you place the gameobject inside an empty gameobject, you can offset it however you need. Then you can just spawn at the rays hit point.

Just created two small demos to demonstrate.
i think this is what you are after, but if not explain why please.

The first one is the object placement without adjusting for normals.
The second one adjusts rotation to normals.

W A S D to move the camera.
Scroll wheel for zoom in and out.
Left click to spawn object.
Objects are bound to 1, 2, 3.

I was only about to help, no reason to call anything stupid.

And i’m not quite sure if you understood what i meant. You can create the models so that the pivot point is at the lowest part of your models. Nothing would be half inside the surface. This way you wouldn’t need any calculation to determine the general height offset. With which kind of objects ( since you said you want to use any kind of object ) would it cause unwanted results?

@lordconstant That looks really good. Maybe I can use parts of my old script to calculate parent’s position.
@Suddoha Because the “ground position” is not always the same, objects can be rotated when it should be other. You were partly right but using parents makes much more sense.

``````using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class Positioner {
public static void FixPosition (GameObject GroundObject, RaycastHit hit){
GameObject positioner = GameObject.Find("Positioner");
Vector3 oldPosition = GroundObject.transform.position;
GroundObject.transform.position = hit.point;
Vector3[] ObjectVertices = GroundObject.GetComponent<MeshFilter>().mesh.vertices;
List<Vector3> ProjectedVertices = new List<Vector3>();
List<float> VerticeDistance = new List<float>();
foreach(Vector3 vertice in ObjectVertices){        // Projects every vertex point to a line from camera
Vector3 linePoint = Vector3.MoveTowards(hit.point, hit.normal, Vector3.Distance(GroundObject.collider.bounds.max, hit.point));
Vector3 ProjectedVertice = Math3d.ProjectPointOnLine(linePoint, (hit.point - linePoint).normalized, GroundObject.transform.TransformPoint(vertice));
}
Vector3 FarthestPoint = ProjectedVertices[VerticeDistance.IndexOf(VerticeDistance.Max())];
positioner.transform.position = FarthestPoint;
GroundObject.transform.SetParent(positioner.transform);
positioner.transform.position = hit.point;
}
}
``````

Seems like hit.point and FarthestPoint are the same. This code needs to be fixed anyway.

OP You have made this thread almost exactly the same like 4 times now. And yet you still haven’t posted a single picture illustrating your problem, despite being prompted to. Pictures REALLY help explain what you want, and whats going wrong.
You also haven’t said if the ground object is always planar (would simplify things a TON), of course, if you’d post an image, this would become apparent…

Here’s a solution:

``````    public GameObject theObject;

void Update () {
if ( Input.GetMouseButtonDown( 0 ) ) {
Ray camRay = camera.ScreenPointToRay( Input.mousePosition ); //Create ray from screen click
Vector3 rayDir = camRay.direction; //Get the ray direction

GameObject gInstance = (GameObject) Instantiate( theObject ); //Instantiate the object
gInstance.transform.position = transform.position; //Move object to camera position - object keeps its scale and rotation

Vector3[] meshPoints = gInstance.GetComponent<MeshFilter>().sharedMesh.vertices; //Get verts of object mesh
for ( int i = 0; i < meshPoints.Length; i++ ) {
meshPoints[i] = gInstance.transform.TransformPoint( meshPoints[i] ); //Transform verts to world space (accounting for prefab rotation and scale)
}

gInstance.SetActive( false ); //Turn object off so it doesnt interfere with raycast

float minDist = float.MaxValue;

for ( int i = 0; i < meshPoints.Length; i++ ) {
Ray pointRay = new Ray(  meshPoints[i], rayDir ); //New ray from each vert, in the direction of the screen ray
//Debug.DrawRay( pointRay.origin, pointRay.direction * 5, Color.red, 4 );
RaycastHit rayHit;
if ( Physics.Raycast( pointRay, out rayHit ) ) {
if ( rayHit.distance < minDist ) {
minDist = rayHit.distance; //Find nearest ray hit
}
}
}

gInstance.SetActive( true ); //Re-enable object

gInstance.transform.position = gInstance.transform.position + rayDir * minDist; //Move object to surface
}
}
``````

Note there are edge-cases where this will not be perfect, where the colliders in the scene (ground) have jagged points
The rays coming down from the object will hit in places around the point, so the object will move too far down. To overcome this (if you really need it to) you will need to raycast back up along the reverse direction from the ground mesh towards the object, to look for a new minDist

Thanks! Do you have any idea why is this happening? (works normally without scaling)

No.
Mainly because it works fine with scales

``````    public GameObject theObject;
float placementScale =1;

void Update () {
if ( Input.GetMouseButtonDown( 0 ) ) {
Ray camRay = camera.ScreenPointToRay( Input.mousePosition ); //Create ray from screen click
Vector3 rayDir = camRay.direction; //Get the ray direction

GameObject gInstance = (GameObject) Instantiate( theObject ); //Instantiate the object
gInstance.transform.localScale = Vector3.one * placementScale; //Change object Scale
gInstance.transform.position = transform.position; //Move object to camera position - object keeps its rotation

Vector3[] meshPoints = gInstance.GetComponent<MeshFilter>().sharedMesh.vertices; //Get verts of object mesh
for ( int i = 0; i < meshPoints.Length; i++ ) {
meshPoints[i] = gInstance.transform.TransformPoint( meshPoints[i] ); //Transform verts to world space (accounting for prefab rotation and scale)
}

gInstance.SetActive( false ); //Turn object off so it doesnt interfere with raycast

float minDist = float.MaxValue;

for ( int i = 0; i < meshPoints.Length; i++ ) {
Ray pointRay = new Ray( meshPoints[i], rayDir ); //New ray from each vert, in the direction of the screen ray
//Debug.DrawRay( pointRay.origin, pointRay.direction * 5, Color.red, 4 );
RaycastHit rayHit;
if ( Physics.Raycast( pointRay, out rayHit ) ) {
if ( rayHit.distance < minDist ) {
minDist = rayHit.distance; //Find nearest ray hit
}
}
}

gInstance.SetActive( true ); //Re-enable object

gInstance.transform.position = gInstance.transform.position + rayDir * minDist; //Move object to surface
}
}

void OnGUI () {
GUILayout.Label( "Place scale: " + placementScale );
placementScale = GUILayout.HorizontalSlider( placementScale, 0, 3, GUILayout.Width( 200 ) );
}
``````