Search code examples
c#unity-game-enginegame-physics

How to move cube in trajectory in Unity3D


I need to move a cube in trajectory motion using dots or arrow type symbol, when drag a mouse to move in a certain position of mouse and its drag power.

I used the code that was used in 2D project to move a ball in trajectory motion but it didn't work.

Here is the code what I tried so for

void Start()
{
    cam = Camera.main;
    cubeClick = GameObject.Find("Cube Click Area");
    trajectoryDots = GameObject.Find("Trajectory Dots");
    rb = Player.GetComponent<Rigidbody>();
    trajectoryDots.transform.localScale = new Vector3(
        initialDotSize, 
        initialDotSize, 
        trajectoryDots.transform.localScale.z);
    for (int dotNumber = 0; dotNumber < 40; dotNumber++)
    {
        Dots[dotNumber] = GameObject.Find("Dot (" + dotNumber + ")");
        if (DotSprite !=null)
        {
            Dots[dotNumber].GetComponent<SpriteRenderer>().sprite = DotSprite;
        }
    }
    for (int dotNumber = NumberOfDots; dotNumber < 40; dotNumber++)
    {
        GameObject.Find("Dot (" + dotNumber + ")").SetActive(false);
    }
    trajectoryDots.SetActive(false);


}


void Update()
{
    Ray camRay = cam.ScreenPointToRay(Input.mousePosition);
    RaycastHit hit;
    //RaycastHit2D hit = Physics2D.Raycast(cam.(Input.mousePosition), Vector2.zero);
    if (Physics.Raycast(camRay, out hit, 100f) && !cubeisClicked2)
    {
        if (hit.collider.gameObject.name == Player.name)
        {
            cubeisClicked = true;
        }
        else
        {
            cubeisClicked = false;
        }
    }
    else
    {
        cubeisClicked = false;
    }

    if (cubeisClicked2)
    {
        cubeisClicked = true;
    }

    if ((rb.velocity.x * rb.velocity.x) + (rb.velocity.y * rb.velocity.y) <= 0.0085f)
    {
        rb.velocity = new Vector2(0f, 0f);
    }
    else
    {
        trajectoryDots.SetActive(false);
    }
    cubePosition = Player.transform.position;
    if ((Input.GetKey(KeyCode.Mouse0) && cubeisClicked) && ((rb.velocity.x == 0f && rb.velocity.y == 0f)))
    {
        cubeisClicked2 = true;
        fingerPosition = cam.ScreenToWorldPoint(Input.mousePosition);
        fingerPosition.z = 0f;
        cubeFingerDiff = cubePosition - fingerPosition;
        ShotForce = new Vector2(cubeFingerDiff.x * shootingPowerX , cubeFingerDiff.y * shootingPowerY);
        if (Mathf.Sqrt((cubeFingerDiff.x * cubeFingerDiff.x) + (cubeFingerDiff.y * cubeFingerDiff.y) )>0.4f)
        {
            trajectoryDots.SetActive(true);

        }
        else
        {
            trajectoryDots.SetActive(false);
        }

        for (int dotNumber = 0; dotNumber < NumberOfDots; dotNumber++)
        {
            x1 = cubePosition.x * ShotForce.x * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift);
            y1 = cubePosition.y * ShotForce.y * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift) - (-Physics.gravity.y / 2f * Time.fixedDeltaTime * Time.fixedDeltaTime * (DotSeparation * dotNumber + dotShift) * (DotSeparation * dotNumber + dotShift));
            Dots[dotNumber].transform.position = new Vector3(x1, -y1, 0);
        }

        if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            cubeisClicked2 = false;
            trajectoryDots.SetActive(false);

            rb.velocity = new Vector2 (ShotForce.x, ShotForce.y);;

        }


    }



}

It doesn't work, It shows the dots in as shown in the .

image

It should move the cube in a way the user drag and point to location to move.


Solution

  • The major problem is that using cam.ScreenToWorldPoint(Input.mousePosition); with a perspective camera always returns the position of the camera (this is a very common mistake), so instead of working with the mouse position vs the cube, you're working with the camera vs the cube.

    You need to use another method to use the position of the mouse considering where the cube is compared to the camera.

    One way to get the behavior you want is to create a plane running through the cube perpendicular to the direction of the camera, and to see where the mouse ray intersects with the plane. Also, set the cubeisClicked2 flag to false, only setting it to true while we actually click on the plane:

    cubePosition = Player.transform.position;
    if ((Input.GetKey(KeyCode.Mouse0) && cubeisClicked) && ((rb.velocity.x == 0f && rb.velocity.y == 0f)))
    {
        cubeisClicked2 = false;
    
        Plane cubePlane = new Plane(cam.transform.position - cubePosition, cubePosition);
    
        // reusing camRay
    
        // Determine if we are even hitting the plane
        float enter = 0.0f;
    
        if (cubePlane.Raycast(camRay, out enter))
        {
            cubeisClicked2 = true;
            fingerPosition  = camRay.GetPoint(enter);
    

    Also, you need to include z components in your force & trajectory calculations. You can probably just use shootingPowerX for the z component's power. There are also the changes that need to be made to the trajectory calculations that I mentioned in the comments:

            cubeFingerDiff = cubePosition - fingerPosition;
            ShotForce = new Vector3(cubeFingerDiff.x * shootingPowerX , cubeFingerDiff.y * shootingPowerY, cubeFingerDiff.z * shootingPowerX );
            if (cubeFingerDiff.magnitude>0.4f)
            {
                trajectoryDots.SetActive(true);
    
            }
            else
            {
                trajectoryDots.SetActive(false);
            }
    
            for (int dotNumber = 0; dotNumber < NumberOfDots; dotNumber++)
            {
                x1 = cubePosition.x + ShotForce.x * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift);
                y1 = cubePosition.y + ShotForce.y * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift) + (Physics.gravity.y / 2f * Time.fixedDeltaTime * Time.fixedDeltaTime * (DotSeparation * dotNumber + dotShift) * (DotSeparation * dotNumber + dotShift));
                z1 = cubePosition.z + ShotForce.z * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift);
                Dots[dotNumber].transform.position = new Vector3(x1, y1, z1);
    

    Then, instead of checking for GetKeyUp(KeyCode.Mouse0) in the same if as determining trajectory, it needs to be next to it and also check if a trajectory was found:

            }
        } 
    }
    
    if (Input.GetKeyUp(KeyCode.Mouse0) && cubeisClicked2)
    {
        cubeisClicked2 = false;
        trajectoryDots.SetActive(false);
    
        rb.velocity = new Vector3 (ShotForce.x, ShotForce.y, ShotForce.z);;
    
    }
    

    Altogether, it might look like this:

    cubePosition = Player.transform.position;
    if ((Input.GetKey(KeyCode.Mouse0) && cubeisClicked) && ((rb.velocity.x == 0f && rb.velocity.y == 0f)))
    {
        cubeisClicked2 = false;
        Plane cubePlane = new Plane(cam.transform.position - cubePosition, cubePosition);
    
        // reusing camRay
    
        // Determine if we are even hitting the plane
        float enter = 0.0f;
        if (cubePlane.Raycast(camRay, out enter))
        {
            cubeisClicked2 = true;
            fingerPosition  = camRay.GetPoint(enter);
    
            cubeFingerDiff = cubePosition - fingerPosition;
            ShotForce = new Vector3(cubeFingerDiff.x * shootingPowerX , cubeFingerDiff.y * shootingPowerY, cubeFingerDiff.z * shootingPowerX );
            if (cubeFingerDiff.magnitude>0.4f)
            {
                trajectoryDots.SetActive(true);
    
            }
            else
            {
                trajectoryDots.SetActive(false);
            }
    
            for (int dotNumber = 0; dotNumber < NumberOfDots; dotNumber++)
            {
                x1 = cubePosition.x + ShotForce.x * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift);
                y1 = cubePosition.y + ShotForce.y * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift) + (Physics.gravity.y / 2f * Time.fixedDeltaTime * Time.fixedDeltaTime * (DotSeparation * dotNumber + dotShift) * (DotSeparation * dotNumber + dotShift));
                z1 = cubePosition.z + ShotForce.z * Time.fixedDeltaTime * (DotSeparation * dotNumber * dotShift);
                Dots[dotNumber].transform.position = new Vector3(x1, y1, z1);
            }
        }         
    }
    
    if (Input.GetKeyUp(KeyCode.Mouse0) && cubeisClicked2)
    {
        cubeisClicked2 = false;
        trajectoryDots.SetActive(false);
    
        rb.velocity = new Vector3 (ShotForce.x, ShotForce.y, ShotForce.z);;
    
    }
    

    Since this calculates the power of the shot on how far away on the cubePlaneyou're pointing at, when the camera is further away, you will be able to shoot more strongly.

    That is probably not the behavior you want, but if the camera is always kept at the same distance from the cube, it shouldn't be a problem.

    If that's not an acceptable limitation, you can probably do something along the lines of dividing ShotForce by the distance between the cube and the camera and increasing shootingPowerX and shootingPowerY as necessary. If you can't find a good solution on your own, it would be best asked in a separate question.