How to translate 2D Mouse Movement to 3D Object's Movement in 3D World?

I create an RTS simulation like ANNO 1602 or let’s say Command & Conquer.

Therefore I need the user to be able to build buildings. This, I managed so far.
But I have a problem with the placement of the buildings.

I want them to appear exactly where the mouse points.

The status quo is, that there is a UI button “build”. When you click on it a building appears. The building will move when you move the mouse. If you click on the ground, the building will stop moving and is placed then.
The problem is, that when they first appear, they are not visible on screen. They are somewhere below the bottom border of the screen. And when you move the mouse they won’t move the same way the mouse moves. More like a V shape movement or so.

Can you tell me how to properly translate the mouse position and movement to the 3D world and the building object?

Here some code:

void Update()
{
    if (!_isPlaced && _temporaryBuilding != null)
    {

        // TODO this part is crappy....
        _mousePos = Input.mousePosition;
        _mousePos.z = Terrain.activeTerrain.SampleHeight(new Vector3(_mousePos.x, 0, _mousePos.y));
        _worldPosition = Camera.main.ScreenToWorldPoint(_mousePos);
        _terrainHeight.y = Terrain.activeTerrain.SampleHeight(_worldPosition);
        if (_temporaryBuilding.GetComponent<ScienceLab>() != null)
        {
            _worldPosition.y = _terrainHeight.y + _temporaryBuilding.transform.lossyScale.y;
        }
        else { _worldPosition.y = _terrainHeight.y + _temporaryBuilding.transform.lossyScale.y / 2; }

        Debug.Log(_worldPosition);
        _temporaryBuilding.transform.position = _worldPosition;

        // on user input
        if (Input.GetMouseButton(0))
        {

            //TODO if (regularly placed?)
            //{

                _isPlaced = true;
                aliveObjects.Add(_temporaryBuilding);
            //}

            _temporaryBuilding = null;
            _isBuildMenuBlocked = false;
        }
    }
}

private GameObject _createBuilding(int type)
{
    if (!_isBuildMenuBlocked)
    {
        _isPlaced = false;
        _temporaryBuilding = (GameObject)Instantiate(typesOfBuildings[type], Input.mousePosition, Quaternion.identity);
        _temporaryBuilding.transform.parent = _dynObjects.transform;
        _isBuildMenuBlocked = true;
    }

    return _temporaryBuilding;
}

EDIT

I now tried to replace lines 5-20 with the following code:

Ray ray;
RaycastHit rayhit;
ray = Camera.main.ScreenPointToRay(Input.mousePosition); 
if(Physics.Raycast(ray, out rayhit, 100, 1<<8)) 
{
    _temporaryBuilding.transform.position = rayhit.collider.transform.position;
}

The result is, that the buildings not longer follow the mouse cursor in some kind of V shape. Actually I do not see them at all. If I click the “build” button and click somewhere and then pause the game, I can find the buildings standing somehere near the end of my terrain. All aligned in a row, depending on where I clicked before.
I guess this_might_ have something to do with their starting position, but also indicates that there is some error with my ray cast code.

I admit, that I do not really know how this raycast thingy works.

2 Answers

2

This did the job for me after setting my buildings to layer 2 until they were finally placed:

Vector3 ScreenToWorld( Vector2 screenPos )
 {
    // Create a ray going into the scene starting 
    // from the screen position provided 
    Ray ray = Camera.main.ScreenPointToRay( screenPos );
    RaycastHit hit;

    // ray hit an object, return intersection point
    if( Physics.Raycast( ray, out hit ) )
        return hit.point;
 
    // ray didn't hit any solid object, so return the 
    // intersection point between the ray and 
    // the Y=0 plane (horizontal plane)
    float t = -ray.origin.y / ray.direction.y;
    return ray.GetPoint( t );
}

(src: FatalFrog.com is for sale | HugeDomains)

Man i just created an account to give you thanks, i have been looking for 2 months a simple and functional answer, you deserve to be canonized!!!

hehe tank you.

ScreeToWorldPoint is using a depth which is from nearclip plane to far… the coords you are giving it are incorrect in this range. Convert that value by determining the distance from the camera to the ground. But this will only work if you have a vertical camera. (Looking straight down and you know the exact distance perpendicular to the surface.

Easier would be to use a raycast to find the location on the surface.

And to use raycast, you'll want to use Camera.ScreenPointToRay, which throws a raycast perpendicular to the camera plane. http://docs.unity3d.com/Documentation/ScriptReference/Camera.ScreenPointToRay.html

you can see the raycast in the upper edited version