I'm getting an IndexOutOfRange exception in my game, but only in a standalone build on Mac OS X. It works fine in the Unity editor on both Windows and OSX, and works fine as a standalone on Windows and Android. On OSX however, I get the following exception:
IndexOutOfRangeException: Array index is out of range.
at WorldController.InstantiateChunk (Vector2i pos, .MeshBuildInfo[] meshes) [0x00072] in /Users/sargunster/Code/Unity Projects/block-game/Assets/Scripts/World/Objects/WorldController.cs:226
at WorldController.FixedUpdate () [0x00060] in /Users/sargunster/Code/Unity Projects/block-game/Assets/Scripts/World/Objects/WorldController.cs:79
Here's the relevant function:
void InstantiateChunk(Vector2i pos, MeshBuildInfo[] meshes)
{
float worldX = pos.x * World.CHUNK_SIZE - .5f;
float worldZ = pos.z * World.CHUNK_SIZE - .5f;
// for each chunk in the column
for (int y = 0; y < World.WORLD_HEIGHT; y++)
{
float worldY = y * World.CHUNK_SIZE - .5f;
// instantiate the chunk
Vector3 position = new Vector3(worldX, worldY, worldZ);
string name = System.String.Format(" Chunk at ({0}, {1}, {2})", pos.x, y, pos.z);
// THIS IS WHERE IT HAPPENED
ChunkRenderer opaque = MakeChunk(position, "Opaque" + name, meshes[y].opaque, opaqueMaterial);
ChunkRenderer transparent = MakeChunk(position, "Transparent" + name, meshes[y].transparent, transparentMaterial);
Vector3i chunkPos = new Vector3i(pos.x, y, pos.z);
opaqueInstances.Add(chunkPos, opaque);
transparentInstances.Add(chunkPos, transparent);
}
}
I followed the references to find where meshes was instantiated, and it should have a length of WORLD_HEIGHT.
MeshBuildInfo[] meshes = new MeshBuildInfo[World.WORLD_HEIGHT];
So, I went in to the debugger to see the exact value of the variable when the exception happened. Debugger output:
> meshes
Evaluation failed.
> 2+2
4
> meshes.Length
Evaluation failed.
> y
0
> position
{(-7168.5, -0.5, 4079.5)}
The index is 0, I can evaluation expressions just fine... except for the array that's not working. I can't get the length of it or its value.
I don't know how to solve this issue. What does "Evaluation Failed" mean? How can the index be out of bounds? Why is this only happening on OSX? How do I fix it?
World.WORLD_HEIGHT is a constant with a value of 16.
After days of being frustrated by this issue, I've finally solved it (sort of). It only occurs when the game is built as x86_64 or Universal. Changing the architecture setting to x86 solved it. Must be a Unity bug.
Related
im currently working on a game and i need to divide a changeable amount of objects in to a limited area. I made a image to explain better:
(I know its a bit difficult to understand)
So i can't find a solution and couldn't figure it out myself. I tried a few ways to multiply and divide the object count with the index and the xMaximum but it didn't work as expected.
This is what i tried:
public float xMin, xMax;
private void UpdateValues()
{
for(int i = 0; i < transform.childCount; i++)
{
float value = xMin;
float maxIndex = transform.childCount;
float index = i;
value = transform.position.x + xMax / (maxIndex * index);
transform.GetChild(i).transform.position = new Vector3(transform.position.x, transform.position.y, value);
}
}
It just gave me the wrong numbers and i can't figure the right way out.
The available variables are:
xMinimum, xMaximum
objectcount
objectindex
If you want it to look exactly how you have shown it on images then it might be a little more tricky. Objects don’t fill the area always the same, so we need to add some exceptions:
if there is only 1 item, then just set it’s position to (xMin + xMax) / 2f
if item is first, and there are more than one, set its position to xMin + (squareWidth / 2f) (or something else if pivot is not in the middle)
if item is last and there are more than one, set its position to xMax - (squareWidth / 2f)
the remaining area, which is of size xMax - 2 * squareWidth need to be divided equally between all remaining objects. Compute the amount remaining objects (which will be all objects - 2 (first and last)). Divide remaining area by number of objects plus 1. Set remaining objects position in a loop, the statement inside will look something like this: value = (i+1) * remainingArea / (transform.childCount - 2 + 1)
That’s the rough list of steps that might help. In this example I assume, that xMin is 0, otherwise you will need to take that into account and for eg. Remaining area won’t be just xMax - 2 * squareWidth, but xMax - xMin - 2 * squareWidth.
Also you might find yourself being off a half a square here and there, but that should be easy to correct.
enter image description here
I can't describe the problem thoroughly because I don't know rhythm game terms.
In the picture
The white cube Note represents the object which the player needs to hit on time.
The green rectangle Line is the representation of the timing. When the Note perfectly lines up with the Line is when the player presses a button to hit the note.
Now the problem is, I cannot seem to find a way to make the Note perfectly line up with the Line using Mathf.Lerp while it still reaches the end.
void GenerateBeat()
{
if (timeItems.Count == 0)
return;
if (timeItems.Peek() <= currentBeatPosition + BeatLookForwardValue)
{
Debug.Log("Item instantiated, Spawned At : " + currentBeatPosition + " Will reach 0 at : " + timeItems.Peek());
GameObject obj = Instantiate(tempSpawnObject);
obj.transform.SetParent(parentObject.transform);
existingBeats.Add(obj);
existingBeatsTime.Add(currentBeatPosition);
timeItems.Dequeue();
}
}
void MoveBeat()
{
// if (timeItems.Count == 0)
// return;
for (int i = 0; i < existingBeats.Count; i++)
{
NoteBeatline beatline = existingBeats[i].GetComponent<NoteBeatline>();
Vector2 StartingPos = new Vector2(0, 800f);
Vector2 EndPos = new Vector2(0, 0);
float offset = (endTime[i] - existingBeatsTime[i]) / 3;
float time = Mathf.InverseLerp(existingBeatsTime[i], endTime[i] + offset, currentBeatPosition);
beatline.Image.rectTransform.anchoredPosition = Vector2.Lerp(StartingPos, EndPos, time);
}
}
[Edit]
Sorry for the lack of clarification.
I'm trying to synchronise Notes by adding extra value to the endTime because existingBeatsTime (keeps track of the time at which the object is spawned) can vary depending on how great BeatLookFOrwardValue is.currentBeatPosition represents how long in beat time has passed since the start of the song, so I cannot manipulate the value.
the endTime value is always a constant, set by me
and items in timeItems are identical to the items in endTime.
the whole background colour of dark cyan is currently 800 units
and the y position of the green horizontal line is 600
so that the Notes can perfectly line up with the green line
when
float time = Mathf.InverseLerp(existingBeatsTime[i], endTime[i] + offset, currentBeatPosition);
is 0.75f.
I did more research and experiment after I posted this,
and found out
float offset = (endTime[i] - existingBeatsTime[i]) / 3;
somehow synchronises the Notes, but I don't understand how.
Please feel free to comment on my explanation.
This is my first time posting a question on StackOverflow and explaining my coding problem to someone else.
I will try to provide more information if it is still lack.
"Currently, Notes reach the end when currentBeatPosition == endTime.
But I want to make them reach the Line when currentBeatPosition == endTime"
So, they are lerping across the correct distance? They are just doing it a bit too fast yeah? So to fix it you can increase the total time over which they are lerping. So they reach the line at endTime and reach the end a little bit later.
get the ratio, distance from start to end / distance from start to line
Multiply the total time they are lerping over by this ratio.
I've finally fixed the synchronisaton problem.
I found out that there's Mathf.LerpUnclamped() which can extrapolate the return value. So I had to change
beatline.Image.rectTransform.anchoredPosition = Vector2.Lerp(StartingPos, EndPos, time);
to
beatline.Image.rectTransform.anchoredPosition = Vector2.LerpUnclamped(StartingPos, EndPos, time);
this. Since it can extrapolate I was able to set the endPos to the position of the green line.
Additionally, I also had to make my own Mathf.InverseLerp() which will be the unclamped version of Mathf.InverseLerp().
public static float InverseLerpUnclamped(float startValue, float maxValue, float inbetweenValue)
{
return (inbetweenValue - startValue) / (maxValue - startValue);
}
Finally, I could simply get it working by these two lines of code.
float time = Tools.InverseLerpUnclamped(existingBeatsTime[i], endTime[i], currentBeatPosition);
beatline.Image.rectTransform.anchoredPosition = Vector2.LerpUnclamped(StartingPos, EndPos, time);
I'm trying to write a function to handle movement within a game I'm programming. What I have nearly works, but there are a couple situations where it breaks down.
I've coded up a minimal demonstrative example, presented below. In this example, I'm trying to calculate the travel of an object, represented by a point, and movement vector. This object's movement path is checked against a collection of polygons, which are broken down into line segments for testing. When this object collides with a line segment, I want it to slide along that segment (rather than stop or bounce away).
To do this, I check along my intended path for collisions, and if I find an intersection, I do a new test from that intersection point along the path of the line segment I've collided with, with the magnitude of the remainder of movement.
The problem arises when we slide along a line segment into a "pocket". Often times, the collision check will pass on both of the line segments that form the pocket, and the object will slip through. Because I'm travelling parallel to one of the line segments, and I'm intersecting with both line segments at an end points, I believe this issue is caused by floating point error. Whether or not it slips through, is caught, or is caught once and then slips through on the second check seems to be totally random.
I'm calculating intersection using a simple algorithm I found here: https://stackoverflow.com/a/20679579/4208739, but I've tried many other algorithms as well. All exhibit the same problems.
(Vector2 is class provided by the Unity library, it just holds x and y coordinates as floats. The Vector2.Dot function just calculates the dot product).
//returns the final destination of the intended movement, given the starting position, intended direction of movement, and provided collection of line segments
//slideMax provides a hard cap on number of slides allowed before we give up
Vector2 Move(Vector2 pos, Vector2[] lineStarts, Vector2[] lineEnds, Vector2 moveDir, int slideMax)
{
int slideCount = 0;
while (moveDir != Vector2.zero && slideCount < slideMax)
{
pos = DynamicMove(pos, lineStarts, lineEnds, moveDir, out moveDir);
slideCount++;
}
return pos;
}
//returns what portion of the intended movement can be performed before collision, and the vector of "slide" that the object should follow, if there is a collision
Vector2 DynamicMove(Vector2 pos, Vector2[] lineStarts, Vector2[] lineEnds, Vector2 moveDir, out Vector2 slideDir)
{
slideDir = Vector2.zero;
float moveRemainder = 1f;
for (int i = 0; i < lineStarts.Length; i++)
{
Vector2 tSlide;
float rem = LineProj(pos, moveDir, lineStarts[i], lineEnds[i], out tSlide);
if (rem < moveRemainder)
{
moveRemainder = rem;
slideDir = tSlide;
}
}
return pos + moveDir * moveRemainder;
}
//Calculates point of collision between the intended movement and the passed in line segment, also calculate vector of slide, if applicable
float LineProj(Vector2 pos, Vector2 moveDir, Vector2 lineStart, Vector2 lineEnd, out Vector2 slideDir)
{
slideDir = new Vector2(0, 0);
float start = (lineStart.x - pos.x) * moveDir.y - (lineStart.y - pos.y) * moveDir.x;
float end = (lineEnd.x - pos.x) * moveDir.y - (lineEnd.y - pos.y) * moveDir.x;
if (start < 0 || end > 0)
return 1;
//https://stackoverflow.com/a/20679579/4208739
//Uses Cramer's Rule
float L1A = -moveDir.y;
float L1B = moveDir.x;
float L1C = -(pos.x *(moveDir.y + pos.y) - (moveDir.x + pos.x)*pos.y);
float L2A = lineStart.y - lineEnd.y;
float L2B = lineEnd.x - lineStart.x;
float L2C = -(lineStart.x * lineEnd.y - lineEnd.x * lineStart.y);
float D = L1A * L2B - L1B * L2A;
float Dx = L1C * L2B - L1B * L2C;
float Dy = L1A * L2C - L1C * L2A;
if (D == 0)
return 1;
Vector2 inter = new Vector2(Dx / D, Dy / D);
if (Vector2.Dot(inter - pos, moveDir) < 0)
return 1;
float t = (inter - pos).magnitude / moveDir.magnitude;
if (t > 1)
return 1;
slideDir = (1 - t) * Vector2.Dot((lineEnd - lineStart).normalized, moveDir.normalized) * (lineEnd - lineStart).normalized;
return t;
}
Is there some way to calculate collision that isn't susceptible to this sort of problem? I imagine I can't totally eradicate floating point error, but is there a way to check that will at least guarantee I collide with ONE of the two line segments at the pocket? Or is there something more fundamentally wrong with going about things in this way?
If anything is unclear I can draw diagrams or write up examples.
EDIT: Having reflected on this issue more, and in response to Eric's answer, I'm wondering if converting my math from floating point to fixed point could solve the issue? In practice I'd really just be converting my values (which can fit comfortably in the range of -100 to 100) to ints, and then performing the math under those constraints? I haven't pieced all the issues together quite yet, but I might give that a try. If anyone has any information about anything like that, I'd be appreciative.
You have a line that, ideally, is aimed exactly at a point, the endpoint of a segment. That means any error in calculation, no matter how small, could say the line misses the point. I see three potential solutions:
Analyze the arithmetic and design it to ensure it is done with no error, perhaps by using extended-precision techniques.
Analyze the arithmetic and design it to ensure it is done with a slight error in favor of collision, perhaps by adding a slight bias toward collision.
Extend the line segment slightly.
It seems like the third would be easiest—the two line segments forming a pocket could just be extended by a bit, so they cross. Then the sliding path would not be aimed at a point; it would be aimed at the interior of a segment, and there would be margin for error.
For my school project im making a 2D tile map with A* algorithm to find shortest path through obstacles. I have used a formula to get heuristic score for the next tiles from http://www.growingwiththeweb.com/2012/06/a-pathfinding-algorithm.html
This is the function for getting heuristic
public static int geth(int cx, int cy, int ex, int ey)
{
//cx = current position x
//cy = current position y
//ex = end (goal) position x
//ey = end (goal) position y
int c = 14;
int d_min = Math.Min(cx - ex, cy - ey);
int d_max = Math.Max(cx - ex, cy - ey);
int h = c * d_min + (d_max - d_min);
if (h < 0) //make h positive in case it's negative
{
h = h * -1;
}
return h;
}
This works when start point is higher on y-axis than end point, but doesn't find the most efficient path when start is lower on y-axis.
I've added a console version of my problem. The most efficient should be diagonally going up, but it takes a wrong path.
(blue 'C' are the nodes checked, green 'P' path made, red 'N' still to be checked, others are not yet reached)
In the linked article, I see this algorithm:
The pipe symbols | around the differences are the absolute value function, and this is represented in C# as Math.Abs.
Distance does not have a sign attached to it. cx - ex will be negative when ex is larger than cx (and likewise with cy - ey) which can cause your Math.Min and Math.Max to choose the wrong dimension for d_min and d_max. You should use Math.Abs to remove the sign from the quantities being compared.
e.g.
int d_min = Math.Min(Math.Abs(cx - ex), Math.Abs(cy - ey));
(And likewise for d_max.)
I am making a small RPG game and I'm currently on window rendering (things like inventory/quest windows).
I draw the actual window with 9 quads textured from a "skin", 4 corners, 4 borders and the middle.
The problem is, at arbitrary coordinates a triangle or two shifts slightly downwards.
It happens consistently, and only if PointClamp or PointWrap is used. Other opions work fine but they give a blurry look.
The same bug actually happens with the green bar above, however I "fixed" it by using Linear instead of Point.
I use this function to convert from pixel coordinates to screen coordinates:
Note: This function works as I originally expected in MonoGame.
public static Vector3 PixelToScreen(GraphicsDevice device, float X, float Y)
{
float xscale = (float)device.Viewport.Width / 2;
float yscale = (float)device.Viewport.Height / 2;
return new Vector3(((int)X / xscale) - 1f, 1f - ((int)Y / yscale), 0);
}
I suspect this function might be the source of my problem. Is there a "right way" to do it?
A picture is worth a thousand words, so here's a screenshot with the problem.
http://i.stack.imgur.com/rNsnH.png
I am quite sure the solution is trivial, but I just can't catch it.
UPDATE
After some more digging and researching, I finally found a solution. It turns out that
it has something to do with how texture pixels are mapped to the screen pixels.
UPDATE #2
After porting this code to MonoGame, I've noticed a different bug where everything looks "blurry". Curiously enough, removing the offset (reverting to the original function) fixes the problem!
The Fix
public static Vector3 PixelToScreen(GraphicsDevice device, float X, float Y)
{
X -= 0.5f; // Offset the "pixel value" by half a pixel
Y -= 0.5f; // To provide "expected results" use negative value
float xscale = (float)device.Viewport.Width / 2;
float yscale = (float)device.Viewport.Height / 2;
return new Vector3((X / xscale) - 1f, 1f - (Y / yscale), 0);
}
More on the topic:
Directly Mapping Texels to Pixels (Direct3D 9)
Understanding Half-Pixel and Half-Texel Offsets
This question is obviously resolved now, but I hope my findings will help someone stuck in the same situation.
The Fix
public static Vector3 PixelToScreen(GraphicsDevice device, float X, float Y)
{
X -= 0.5f; // Offset the "pixel value" by half a pixel
Y -= 0.5f; // To provide "expected results" use negative value
float xscale = (float)device.Viewport.Width / 2;
float yscale = (float)device.Viewport.Height / 2;
return new Vector3((X / xscale) - 1f, 1f - (Y / yscale), 0);
}
Do not use these offsets in MonoGame as it somehow takes care of the matters internally.