我有一个用 C# 实现的系统,用于占据整个屏幕的网格。网格块的大小为 16x16。问题是,有一个拖放系统可以根据鼠标拖动的方向移动块。
这部分代码确保每当 Vector2 的任何轴超过 ChunkSize(16 个单位)时都会重置偏移量:
var ui = _advancedGroupUI;
var uiRect = ui.Rect.Round(ChunkSize);
var co = ui.CurrentOffset;
var bx = Mathf.Abs(co.x) > ChunkSize;
var by = Mathf.Abs(co.y) > ChunkSize;
var offset = co.ToInt(ChunkSize);
var resetDelta = bx || by;
if (resetDelta)
{
var x = co.x;
var y = co.y;
if (bx)
x = 0;
if (by)
y = 0;
ui.CurrentOffset = new Vector2(x, y);
}
ToInt
的实现如下所示:
public static Vector2Int ToInt(this Vector2 f, float r)
{
var x = f.x >= 0 ? Mathf.FloorToInt(f.x / r) : Mathf.CeilToInt(f.x / r) * (int)r;
var y = f.y >= 0 ? Mathf.FloorToInt(f.y / r) : Mathf.CeilToInt(f.y / r) * (int)r;
return new Vector2Int(x, y);
}
之后,代码将屏幕外部的块重新定位到屏幕内部与拖放发生位置相反的一侧。例如,如果您从右向左拖动,左侧的块将移出屏幕边界,必须重新定位到右侧,反之亦然。从上到下拖动时也是如此:
var (columns, rows) = GetColumnsAndRows();
var length = columns * rows;
for (var key = 0; key < length; key++)
{
var (sx, sy) = _jobManager.GetScreenPosition(key);
var (x, y) = IndexerUtils.To2DTuple(key, columns);
var cx = x * ChunkSize;
var cy = y * ChunkSize;
// move all chunks to the new offset
var @new = new Vector2Int(sx - offset.x, sy - offset.y);
// destroy if it's out of screen
var outOfScreen = !uiRect.Contains(@new);
if (outOfScreen)
{
var nx = @new.x % screenWidth;
var ny = @new.y % screenHeight;
var position = new Vector2Int(nx, ny);
var worldPosition = new long2(nx + totalOffset.x, ny + totalOffset.y);
_jobManager.SetScreenPosition(key, position);
_jobManager.SetWorldPosition(key, worldPosition);
_jobManager.ReleaseAt(key);
}
else
_jobManager.SetScreenPosition(key, @new);
}
问题出在这行代码:
var worldPosition = new long2(nx + totalOffset.x, ny + totalOffset.y);
这部分无法正常工作。我们有多个问题,让我解释一下:
重点是,当我们更新
ui.CurrentOffset
时,它就是拖动事件的总增量值。
按以下方式处理:
private bool HandleDragEvent(bool changed)
{
if (Event.current.type == EventType.MouseDrag &&
(Event.current.button == 0 && Event.current.modifiers == EventModifiers.Alt ||
Event.current.button == 2))
{
var delta = Event.current.delta;
var lastPosition = CurrentOffset;
var position = CurrentOffset - (delta) * dragForce;
_zoomCoordsOrigin = position;
CurrentOffset += position - lastPosition;
changed = delta != Vector2.zero;
Event.current.Use();
}
return changed;
}
我们读取它来分配 worldPosition,但这给了我视觉上位于同一轴(Y 轴)上的块,它们没有相同的 Y 轴值,因为它们受到拖动增量值的影响,例如:
注意视频中的方形部分。
https://gyazo.com/4f73676ec18010e07d8c1c803f70d2c6.mp4
正如您所看到的,同一行上的值从 -32 下降到 -48,然后再下降到 -64,这是不正确的。
这个视频很容易解释另一个问题:
https://gyazo.com/5933553747d6ca79ab53333ba07b8633.mp4
如果我们拖放以发现屏幕的左上角区域,一切都会顺利,但如果我们尝试拖动到屏幕的右下角部分,则什么也不会发生。
如果需要对代码进行任何说明,我可以提供它以帮助更好地理解问题。
一个问题是你的
ToInt
函数:乘法比三元运算符具有更高的优先级(优先级),因此对于 (int)r
的情况,你只能乘以 f.x < 0
。您需要添加括号:
public static Vector2Int ToInt(this Vector2 f, float r)
{
var x = (f.x >= 0 ? Mathf.FloorToInt(f.x / r) : Mathf.CeilToInt(f.x / r)) * (int)r;
var y = (f.y >= 0 ? Mathf.FloorToInt(f.y / r) : Mathf.CeilToInt(f.y / r)) * (int)r;
return new Vector2Int(x, y);
}
这个错误会导致你的
offset
不是 16 的倍数:
new Vector2(18.8f, -16.2f).ToInt(16) // => (1,-16)
这可能会扰乱您的渲染代码,因为您将在一个方向上移动块 1 的倍数,而在另一个方向上移动 16 的倍数。