Reputation: 2461
I have a system implemented in C# for a grid that occupies the entire screen. The grid chunks have a size of 16x16. The thing is, there’s a drag-and-drop system to move the chunks based on the direction of the mouse drag.
This part of the code ensures that the offset resets whenever any axis of the Vector2 exceeds the ChunkSize (16 units):
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);
}
The implementation of ToInt
looks like this:
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);
}
After that, the code repositions the chunks that are outside the screen back inside the screen on the opposite side from where the drag-and-drop occurs. For instance, if you’re dragging from right to left, the chunks on the left will move out of the screen bounds and must be repositioned to the right, and vice versa. The same applies when dragging from top to bottom:
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);
}
The issue lies in this line of code:
var worldPosition = new long2(nx + totalOffset.x, ny + totalOffset.y);
This part isn’t working correctly. We have multiple issues, let me explain:
The point is that when we update ui.CurrentOffset
that is the the total delta value of the drag event.
Which is handled the following way:
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;
}
We read it to assign worldPosition, but this gives me that chunks which are on the same axis visually (Y axis), they don't have the same Y-axis value, because they are affected by the drag delta value, for example:
Pay attention to the squared part on the video.
https://gyazo.com/4f73676ec18010e07d8c1c803f70d2c6.mp4
As you can see the values goes down, from -32 to -48 and then to -64 on the same line, this is not correct.
The other problem is explained easily with this video:
https://gyazo.com/5933553747d6ca79ab53333ba07b8633.mp4
If we drag and drop to discover top left zone of the screen everything goes well, but if we try to drag to the bottom right part of the screen nothing happens.
If any clarification about the code is needed, I can provide it to help better understand the problem.
Upvotes: 0
Views: 27
Reputation: 3346
One problem is your ToInt
function: multiplication has a higher precedence (priority) over the ternary operator, so your only multiplying by (int)r
for the case when f.x < 0
. You need to add parentheses:
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);
}
(or even better, return (((Vector2Int) f) / r) * r;
...)
This bug is causing your offset
to be numbers other than multiples of 16:
new Vector2(40.8f, -16.2f).ToInt(16) // => actual: (2,-16), expected: (32,-16)
Which might be what's messing up your rendering code, since you would be moving the chunks by multiples of 1 in one direction, and multiples of 16 in the other.
Upvotes: 1