Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Why does the cursor gameobject not go in the correct position?

Discussion in 'Scripting' started by babaplowman0, Jun 2, 2023.

  1. babaplowman0

    babaplowman0

    Joined:
    Jun 29, 2021
    Posts:
    4
    Hello! I am creating a rhythm game, and I need to create a level editor. However, I need to make a cursor that actually works, and goes to the correct position so its easier to place blocks and stuff like that. I have the code right here.

    Code (CSharp):
    1. using System.Collections;
    2.  
    3. using System.Collections.Generic;
    4.  
    5. using UnityEngine;
    6.  
    7. using DG;
    8.  
    9. using DG.Tweening;
    10.  
    11. using Unity.Mathematics;
    12.  
    13.  
    14.  
    15. public class ObjPlacing : MonoBehaviour
    16.  
    17. {
    18.  
    19. private Vector3 mousePos;
    20.  
    21. private Vector3 objectPos;
    22.  
    23. private Vector3 confirmedPos;
    24.  
    25. private Vector3 originConfirmedPos;
    26.  
    27. private Vector3 placeBlockPos;
    28.  
    29. private Vector3 originLevelPos;
    30.  
    31. private Tween moveTween;
    32.  
    33.  
    34.  
    35. public bool drag = false;
    36.  
    37. public bool hasChanged;
    38.  
    39.  
    40.  
    41. public RectTransform randomTester;
    42.  
    43.  
    44.  
    45. public RectTransform levelTransform;
    46.  
    47. // Start is called before the first frame update
    48.  
    49. void Start()
    50.  
    51. {
    52.  
    53. }
    54.  
    55.  
    56.  
    57. // Update is called once per frame
    58.  
    59. void Update()
    60.  
    61. {
    62.  
    63. if (Input.GetMouseButtonDown(0))
    64.  
    65. drag = true;
    66.  
    67. else if (!Input.GetMouseButton(0))
    68.  
    69. drag = false;
    70.  
    71.  
    72.  
    73. mousePos = Input.mousePosition;
    74.  
    75. //Debug.Log(mousePos);
    76.  
    77. //randomTester.transform.position = confirmedPos;
    78.  
    79. if (!drag)
    80.  
    81. {
    82.  
    83. originLevelPos = levelTransform.position;
    84.  
    85. calculatePos();
    86.  
    87. moveTween = randomTester.DOMove(confirmedPos, 0.5f).SetEase(Ease.OutQuart);
    88.  
    89. moveTween.Complete();
    90.  
    91. // hasKilled = false;
    92.  
    93. }
    94.  
    95. //else if (drag)
    96.  
    97. //{
    98.  
    99. // hasChanged = false;
    100.  
    101. // moveTween = randomTester.DOMove(confirmedPos - (originLevelPos - levelTransform.position), 0.01f).SetEase(Ease.OutQuart);
    102.  
    103. // }
    104.  
    105.  
    106.  
    107. Debug.Log((originLevelPos - levelTransform.position));
    108.  
    109. }
    110.  
    111. private void calculatePos()
    112.  
    113. {
    114.  
    115. Vector3 levelPos = levelTransform.position;
    116.  
    117. Vector3 canvasPos = GameObject.FindObjectOfType<Canvas>().transform.position;
    118.  
    119.  
    120.  
    121. int gridSize = 50;
    122.  
    123. confirmedPos = new Vector3(
    124.  
    125. Mathf.RoundToInt((mousePos.x) / gridSize) * gridSize,
    126.  
    127. Mathf.RoundToInt((mousePos.y) / gridSize) * gridSize,
    128.  
    129. 0
    130.  
    131. );
    132.  
    133.  
    134.  
    135.  
    136.  
    137. }
    138.  
    139. }


    My code is a little messy, because I am a very new scripter. If anyone can help, I would appreciate it :)

    Heres the video:

     
  2. babaplowman0

    babaplowman0

    Joined:
    Jun 29, 2021
    Posts:
    4
    Oh yeah, and if you would like any more code on how the level moves, please let me know!
     
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    How you're getting the grid position is fine, however you're forgetting to compensate for the moving background.

    Obviously the grid moves along with the background, so what you're seeing is grid being stuck to one place, and so it loses its correspondence with the actual world space.

    When working like this you really want to make sure that the space you're working with is always the right one. This is a prime example of what can get wrong if you compute your points each time like it's the first time.

    Instead try to think like this:
    Your image movement is relative to some world space. In fact, it dictates the world space, right?
    The way I'm seeing this, your background image doesn't move, you effectively move your view around, am I correct?
    So the essence of this is that your just moving a point of origin around. So if you move the image down and left, it was the origin that got moved down and left.

    Your mouse movement should occur relative to this world space. So you need to apply there the same origin shift before you attempt to compute your grid coordinates. But keep in mind you want to do this inversely, so effectively you apply the origin shift by subtracting it from the mouse coordinates. (And you likely do this already.)

    Finally your grid is also offset so that it aligns to this world origin. Apply the same logic there, however this time you simply add the origin shift.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    Here are some grid functions I wrote a couple of days ago. You can find an example with offset as well.

    (MathF functions are in System namespace btw, but you can use Unity's Mathf for this, it's fine.)
     
  5. babaplowman0

    babaplowman0

    Joined:
    Jun 29, 2021
    Posts:
    4
    Ah, okay! I see. Thank you, I'll implement what you have said in a little. I'll update you if it has worked
     
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    In case I was a little vague about what exactly needs to change:

    You're dealing with two spaces, one is the world space and the other is the view space.
    In the world space, both background and grid are aligned and don't move.

    Because both spaces are in 2D and axis-aligned (that means they're unrotated) and don't appear to be scaled, all you need to capture the transformation between them is the world origin shift.

    You have three points of interest in the world space: the background's origin, the mouse cursor, and the grid origin.
    Background and grid are both at the (0, 0) of this space, all you need to compute properly is the correct mouse position.

    You basically convert it from some initial input space to view space, then from view space to world space, and then you have the real position of the mouse cursor.

    Finally, you want to show all these things, and to do this you get everything back from world space to view space.

    The easiest way to work like this is to make these conversions upfront, and then rely on them forever. You will never confuse yourself if you do it like this and your actual code will always deal with one space: the world space, so it's super friendly to read and understand.

    Code (csharp):
    1. Vector2 toWorldSpacePoint(Vector2 p, Vector2 viewOffset) {
    2.   return p - viewOffset;
    3. }
    4.  
    5. Vector2 toViewSpacePoint(Vector2 p, Vector2 viewOffset) {
    6.   return p + viewOffset;
    7. }
    You can treat the mouse like this
    (where
    mouseInput
    are coordinates in view space)
    Code (csharp):
    1. var mousePos = toWorldSpacePoint(mouseInput, viewOffset);
    2. var snappedPos = snapToGrid(mousePos, gridScale);
    Where grid is (assuming (0, 0) as its world origin)
    Code (csharp):
    1. Vector2 snapToGrid(Vector2 pos, Vector2 scale) {
    2.   return new Vector2(snap(pos.x, scale.x), snap(pos.y, scale.y));
    3.   static float snap(float n, float s) => s * (int)MathF.Round(n / s);
    4. }
    Now you know where to display the background and the highlighter
    Code (csharp):
    1. background.transform.localPosition = toV3(toViewSpacePoint(Vector2.zero, viewOffset));
    2. highlighter.transform.localPosition = toV3(toViewSpacePoint(snappedPos, viewOffset));
    where toV3 is just
    Code (csharp):
    1. Vector3 toV3(Vector2 p) {
    2.   return new Vector3(p.x, p.y, 0f);
    3. }
    (If you intend to always work in XY, you can omit this entirely, though I don't recommend it.)

    Compared to ad hoc adding, subtracting ... hopefully you can see how super readable this is.
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    And of course, if you intend to do this backward, and instead of moving these objects, you wish to move only the camera inversely, you can very easily change this to achieve that

    Code (csharp):
    1. background.transform.localPosition = toV3(Vector2.zero);
    2. highlighter.transform.localPosition = toV3(snappedPos);
    3. camera.transform.localPosition = toV3(toWorldSpacePoint(Vector2.zero, viewOffset));
     
  8. babaplowman0

    babaplowman0

    Joined:
    Jun 29, 2021
    Posts:
    4
    Ohhhhh thank you very much! I understand now.