【问题标题】:Converting screen mouse position to local mouse position between -1 and 1将屏幕鼠标位置转换为 -1 和 1 之间的本地鼠标位置
【发布时间】:2016-04-08 05:08:14
【问题描述】:

我在 Unity 中有一个 2D 瓦片游戏项目,其中每个瓦片的大小为(例如)32 x 32 像素,但在网格中瓦片映射到完整的整数坐标,例如 tile0 位于 @987654322 @,tile1 位于 x1 y0tile2 位于 x2 y0tile10 位于 x2 y1,依此类推(如果我没记错的话,这个映射是由于将 pixel per unit size 设置为将精灵表设置为例如 32px,对吗?)

无论如何,玩家应该能够用鼠标向左/向右/向上/向下拖动瓷砖,我希望瓷砖随着鼠标位置一起拖动。我遇到的问题是我不知道如何将全局鼠标坐标转换为拖动图块的局部空间,特别是转换为 -1 和 1 之间的范围,因为拖动距离已标准化为该范围。

在我的Update() 方法中,我有这个代码:

if (_isMouseDown && sourceTile != null)
{
    _isDraggingTile = true;
    Vector3 dragDistance = Input.mousePosition - _originMousePosition;
    dragDistance.Normalize();

    GridCell targetCell;

    /* Check mouse up/down drag. */
    float f = Vector3.Dot(dragDistance, Vector3.up);
    if (f >= 0.5f)
    {
        targetCell = sourceTile.gridCell.upNeighbor;
    }
    else if (f <= -0.5f)
    {
        targetCell = sourceTile.gridCell.downNeighbor;
    }
    else
    {
        /* Check mouse left/right drag. */
        f = Vector3.Dot(dragDistance, Vector3.right);
        targetCell = f >= 0.5f ? sourceTile.gridCell.rightNeighbor : sourceTile.gridCell.leftNeighbor;
    }

    if (Vector3.Distance(_originMousePosition, Input.mousePosition) > _globals.tileDragThreshold)
    {
        if (targetCell != null)
        {
            SetTargetTile(targetCell.tile);
            ResolveMove();
        }
    }
}

代码检查鼠标拖动到哪个方向,并在某个阈值后将拖动的图块切换到相邻网格单元中的图块 (targetCell)。我想要实现的是拖动的图块跟随鼠标位置直到交换发生。

谁能给我一个提示,告诉我如何将鼠标位置转换成这个,以便相应地调整拖动的图块的位置?

【问题讨论】:

    标签: c# unity3d 2d mouse game-engine


    【解决方案1】:

    您可以在图块上添加一个不可见的平面,以便您可以使用光线投射和命中位置来玩弄您的图块。

    考虑到您所有的瓷砖位置都存储在一个集合中,您可以在鼠标按下时找到最近的瓷砖。这将成为您当前的磁贴。然后你用飞机上演员给定的位置移动它。

    当鼠标位置离当前图块的初始位置太远时,您将再次迭代集合以找到最近的图块位置并将图块捕捉到该新位置。您还需要更新上下文,以便现在将图块分配到该新位置。

    伪代码类似于:

    Tile currentTile = null;
    bool movement = false;
    void Update(){
    
        if(Input.GetMouseButtonDown(0)){
           Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
           RaycastHit hit;
           Physics.Raycast(ray, out hit);
           currentTile = GetClosestTile(hit.position); 
           movement = true;
        }
        else if (Input.GetMouseButton(0))
        {
           if(movement == false){return;}
           Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
           RaycastHit hit;
           Physics.Raycast(ray, out hit);
           if(PositionOutOfBound(hit.position) == true){
                Tile newTile = GetClosestTile(hit.position);
                newTile.currentSprite = currentTile.currentSprite;
                currentTile.currentSprite = null;
                movement = false;
                return;
           }
           currentTile.currentSprite.transform.position = hit.position;
        }
    }
    

    这个想法是,当你按下时,演员会找到最近的瓷砖并分配给当前瓷砖。

    下一帧,当你按住时,它会进入第二部分,这一次它会查看位置是否与 PositionOutOfBound 太远。如果足够远,您可以重用 GetClosestTile。返回的图块接收精灵(我假设您为图块使用精灵),currentTile 将精灵移除并将移动设置为 false 以避免再次执行操作。

    如果您距离初始图块 (currentTile) 不够远,PositionOutOfBound 将返回 false,您只需让精灵随鼠标位置移动。您不想移动图块,而只想移动与其链接的精灵。

    同样,这只是扩展的伪代码,但您可以轻松找到有关如何查找最近的示例等。

    【讨论】:

    • 我正在尝试遵循您的代码示例,但我的 hit.point 始终为 (0.0, 0.0, 0.0)。知道为什么吗?我是否必须在瓦片网格前面添加一个实际的 3D 平面(与网格大小相同)?
    • 是的,你需要一架带有对撞机的飞机。您还可以检查您是否真的在 raycast 上获得成功,我只是假设它应该发生。
    • 嗯,这个平面网格的解决方案真的很麻烦!有什么方法可以通过 c# 添加 MeshFilter 组件并将 Unity 默认平面加载到其中,还是只能在编辑器中创建?因为如果不编写脚本,它就毫无用处。
    • 是的,你可以。所以你必须创建一个游戏对象,然后添加你自己的平面网格(很可能是从搅拌机或其他类型导入的),然后向它添加一个对撞机(确保等待帧结束或对撞机将具有默认大小)。所有这些都在协程中,因为您需要等待帧结束。如果您需要执行特定的图层或标签,那么您还需要设置它们。不过,我真的不明白添加一个平面和设置一次值是多么麻烦。也许可以扩展这个令人烦恼的问题,以便我们可以更好地帮助您。
    • 感谢您迄今为止的帮助!这很麻烦,因为我们不能通过脚本将统一的默认包含平面加载到 MeshFilter 中。 (这不仅麻烦,而且非常荒谬!)无论如何,我使用来自answers.unity3d.com/questions/139808/… 的第二个脚本来生成平面网格,我正在尝试使用它。到目前为止工作,但我还没有让它适合屏幕的大小。
    【解决方案2】:

    我不知道规格,但我有一个没有rigidbody 的拖动对象的通用代码。检查是否可以根据需要自定义此代码。

    void Update ()
        {
            if (Input.GetMouseButton (0)) {
    
                Vector3 point = Camera.main.ScreenToWorldPoint (Input.mousePosition);
    
                transform.position = Vector3.MoveTowards (transform.position, new Vector3 (point.x, point.y, transform.position.z), 1);
    
    
            }
    
        }
    

    此代码当前正在移动附加此脚本的GameObject,因为transform.position 代表当前的GameObject's transform

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-06
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多