static private Dir vec3ToDir(Vec3 pos) { /* XXX: Only check axis X and Z */ int[] axisOrder = { 0, 2 }; Dir[,] perAxis = { { Dir.Left, Dir.Right }, { Dir.Bottom, Dir.Top }, { Dir.Back, Dir.Front }, }; float absDist = 0.0f; Dir d = Dir.None; /* Select the direction of the axis with the greater distance, so the * entity will follow the last position of the targeted object */ for (int i = 0; i < axisOrder.Length; i++) { int axis = axisOrder[i]; if (Math.Abs(pos[axis]) > absDist) { absDist = Math.Abs(pos[axis]); if (pos[axis] < 0) { d = perAxis[axis, 0]; } else if (pos[axis] > 0) { d = perAxis[axis, 1]; } } } return(d); }
int GetWrapedTable(int setSquare, int tempDir, int tableSize, int tableRows) { var tempSquare = (Math.Abs(tempDir) == 4) ? -1 * (int)Math.Sign(tempDir) : (Math.Abs(tempDir) % 5) * (int)Math.Sign(tempDir); if ((setSquare % tableRows) + tempSquare == -1 || (setSquare % tableRows) + tempSquare == tableRows) { tempSquare = (tableRows - 1) * ((((setSquare % tableRows) + tempSquare) == tableRows) ? -1 : 1); } setSquare += tempSquare; if (Math.Abs(tempDir) != 1 && Math.Abs(tempDir) != 0) { tempSquare = tableRows * (int)Math.Sign(tempDir); setSquare += tempSquare; } if (setSquare < 0) { setSquare = tableSize - Math.Abs(setSquare); } setSquare %= tableSize; return(setSquare); }
/** * Check every axis on every gamepad to ensure that everything is * as stable as possible. * * @return true if the axis are stable, false otherwise */ static public bool TrainAxisStable() { bool stable = true; if (Input.axisRest == null) { Input.axisRest = new float[gamepadNum * gamepadAxisNum]; for (int i = 1; i < gamepadNum * gamepadAxisNum; i++) { Input.axisRest[i] = 0.0f; } } for (int gpIdx = 1; gpIdx < gamepadNum; gpIdx++) { for (int gpAxis = 0; gpAxis < gamepadAxisNum; gpAxis++) { int i = gpIdx * gamepadAxisNum + gpAxis; string name = $"joystick {gpIdx} axis {gpAxis}"; float cur = DefInput.GetAxisRaw(name); float rest = Input.axisRest[i]; float diff = Math.Abs(rest - cur); Input.axisRest[i] = 0.99f * (cur * 0.75f + rest * 0.25f); stable = (stable && diff < 0.05f); } } return(stable); }
/// <summary> /// Is the plane hits part of the bound? /// </summary> public bool IsIntersecting(Plane4 plane) { var r = Vector4.Dot(extent, Vector4.Abs(plane.normal)); var s = Vector4.Dot(center, plane.normal) - plane.distance; return(Math.Abs(s) <= r); }
private static int StepDistance(Point pointA, Point pointB) { int distanceX = Math.Abs(pointA.X - pointB.X); int distanceY = Math.Abs(pointA.Y - pointB.Y); return(distanceX + distanceY); }
private void OptimizeAtlas() { for (int atlasIndex = 0; atlasIndex < atlassedMaterials.Count; atlasIndex++) { var material = atlassedMaterials[atlasIndex]; Vector2 usedArea = new Vector2(0, 0); for (int atlasElementIndex = 0; atlasElementIndex < material.materialFragments.Count; atlasElementIndex++) { if (material.materialFragments[atlasElementIndex].atlasRegion.xMax > usedArea.x) { usedArea.x = material.materialFragments[atlasElementIndex].atlasRegion.xMax; } if (material.materialFragments[atlasElementIndex].atlasRegion.yMax > usedArea.y) { usedArea.y = material.materialFragments[atlasElementIndex].atlasRegion.yMax; } } //Headless mode ends up with zero usedArea if (Mathf.Approximately(usedArea.x, 0f) || Mathf.Approximately(usedArea.y, 0f)) { material.cropResolution = new Vector2(0.0f, 0.0f); return; } Vector2 tempResolution = new Vector2(umaGenerator.atlasResolution, umaGenerator.atlasResolution); bool done = false; while (!done && Mathf.Abs(usedArea.x) > 0.0001) { if (tempResolution.x * 0.5f >= usedArea.x) { tempResolution = new Vector2(tempResolution.x * 0.5f, tempResolution.y); } else { done = true; } } done = false; while (!done && Mathf.Abs(usedArea.y) > 0.0001) { if (tempResolution.y * 0.5f >= usedArea.y) { tempResolution = new Vector2(tempResolution.x, tempResolution.y * 0.5f); } else { done = true; } } material.cropResolution = tempResolution; } }
private float getAxisPerc(float val) { float diff = val - this.rest; if (val < this.rest) { return(Math.Abs(diff / (1.0f + rest))); } else if (val > this.rest) { return(diff / (1.0f - rest)); } else { return(0.0f); } }
/// <summary> /// Computes Worley/Voroni noise for the given seed. /// </summary> /// <param name="distsToValue"> /// Takes in the closest and second-closest distances and outputs a noise value from them. /// </param> public static float WorleyNoise(float seed, Func <float, float, float> distsToValue) { //Get the min corner of each of the 9 grid cells near the seed value. float posMid = Mathf.Floor(seed), posLess = posMid - 1.0f, posMore = posMid + 1.0f; //Get a random point inside each of these cells // and get the distance from the seed pos to the two closest points. float min1 = float.PositiveInfinity, min2 = float.PositiveInfinity; Utils.GetWorleyMins(ref min1, ref min2, Mathf.Abs(seed - (posMid + WhiteNoise(posMid)))); Utils.GetWorleyMins(ref min1, ref min2, Mathf.Abs(seed - (posLess + WhiteNoise(posLess)))); Utils.GetWorleyMins(ref min1, ref min2, Mathf.Abs(seed - (posMore + WhiteNoise(posMore)))); //Filter these distance values into some noise value. return(distsToValue(min1, min2)); }
/// <summary> Gets 1B representation of a float value, the float must be in range: 0.9921875 >= a >= -0.9921875, precision 0.0078125 </summary> private static byte Get1ByteSignedFloatSmallerThan1(float val) { float valAbs = Mathf.Abs(val); if (valAbs > 1) { throw new FormatException($"Error, can only represent values in the following ranges: x < 1, value given: {valAbs}"); } byte byteVal = 0; if (val < 0) //Add negative sign bit { byteVal += 128; } GetByteFromAbsFloatSmallerThan1(valAbs, ref byteVal, 6); return(byteVal); }
static void DrawVO(Vector2 circleCenter, float radius, Vector2 origin) { float alpha = Mathf.Atan2((origin - circleCenter).y, (origin - circleCenter).x); float gamma = radius / (origin - circleCenter).magnitude; float delta = gamma <= 1.0f ? Mathf.Abs(Mathf.Acos(gamma)) : 0; Draw.Debug.CircleXZ(FromXZ(circleCenter), radius, Color.black, alpha - delta, alpha + delta); Vector2 p1 = new Vector2(Mathf.Cos(alpha - delta), Mathf.Sin(alpha - delta)) * radius; Vector2 p2 = new Vector2(Mathf.Cos(alpha + delta), Mathf.Sin(alpha + delta)) * radius; Vector2 p1t = -new Vector2(-p1.y, p1.x); Vector2 p2t = new Vector2(-p2.y, p2.x); p1 += circleCenter; p2 += circleCenter; Debug.DrawRay(FromXZ(p1), FromXZ(p1t).normalized *100, Color.black); Debug.DrawRay(FromXZ(p2), FromXZ(p2t).normalized *100, Color.black); }
private void AxialInput() { offset = axialOffset; if (M.Abs(Input.GetAxis("Horizontal")) > 0 || M.Abs(Input.GetAxis("Vertical")) > 0) { input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); } G.DrawWireCube(offset, new V3(2, 2, 0)); G.color = C.magenta; G.DrawLine(offset, (Vector3)input + offset); var x = input.x; var y = input.y; G.color = C.red; G.DrawLine(offset, offset + V3.right * x); G.color = C.green; G.DrawLine(offset, offset + V3.up * y); G.color = C.black * .5f; G.DrawRay(offset, new V3(x, y).normalized); }
protected override void Inspector() { // Find all properties var points = FindProperty("points"); var legacyMode = FindProperty("legacyMode"); // Get a list of inspected components scripts = new GraphUpdateScene[targets.Length]; targets.CopyTo(scripts, 0); EditorGUI.BeginChangeCheck(); // Make sure no point arrays are null for (int i = 0; i < scripts.Length; i++) { scripts[i].points = scripts[i].points ?? new PF.Vector3[0]; } if (!points.hasMultipleDifferentValues && points.arraySize == 0) { if (scripts[0].GetComponent <PolygonCollider2D>() != null) { EditorGUILayout.HelpBox("Using polygon collider shape", MessageType.Info); } else if (scripts[0].GetComponent <Collider>() != null || scripts[0].GetComponent <Collider2D>() != null) { EditorGUILayout.HelpBox("No points, using collider.bounds", MessageType.Info); } else if (scripts[0].GetComponent <Renderer>() != null) { EditorGUILayout.HelpBox("No points, using renderer.bounds", MessageType.Info); } else { EditorGUILayout.HelpBox("No points and no collider or renderer attached, will not affect anything\nPoints can be added using the transform tool and holding shift", MessageType.Warning); } } DrawPointsField(); EditorGUI.indentLevel = 0; DrawPhysicsField(); PropertyField("updateErosion", null, "Recalculate erosion for grid graphs.\nSee online documentation for more info"); DrawConvexField(); // Minimum bounds height is not applied when using the bounds from a collider or renderer if (points.hasMultipleDifferentValues || points.arraySize > 0) { PropertyField("minBoundsHeight"); Clamp("minBoundsHeight", 0.1f); } PropertyField("applyOnStart"); PropertyField("applyOnScan"); DrawWalkableField(); DrawPenaltyField(); DrawTagField(); EditorGUILayout.Separator(); if (legacyMode.hasMultipleDifferentValues || legacyMode.boolValue) { EditorGUILayout.HelpBox("Legacy mode is enabled because you have upgraded from an earlier version of the A* Pathfinding Project. " + "Disabling legacy mode is recommended but you may have to tweak the point locations or object rotation in some cases", MessageType.Warning); if (GUILayout.Button("Disable Legacy Mode")) { for (int i = 0; i < scripts.Length; i++) { Undo.RecordObject(scripts[i], "Disable Legacy Mode"); scripts[i].DisableLegacyMode(); } } } if (scripts.Length == 1 && scripts[0].points.Length >= 3) { var size = scripts[0].GetBounds().size; if (Mathf.Min(Mathf.Min(Mathf.Abs(size.x), Mathf.Abs(size.y)), Mathf.Abs(size.z)) < 0.05f) { EditorGUILayout.HelpBox("The bounding box is very thin. Your shape might be oriented incorrectly. The shape will be projected down on the XZ plane in local space. Rotate this object " + "so that the local XZ plane corresponds to the plane in which you want to create your shape. For example if you want to create your shape in the XY plane then " + "this object should have the rotation (-90,0,0). You will need to recreate your shape after rotating this object.", MessageType.Warning); } } if (GUILayout.Button("Clear all points")) { for (int i = 0; i < scripts.Length; i++) { Undo.RecordObject(scripts[i], "Clear points"); scripts[i].points = new PF.Vector3[0]; scripts[i].RecalcConvex(); } } if (EditorGUI.EndChangeCheck()) { for (int i = 0; i < scripts.Length; i++) { EditorUtility.SetDirty(scripts[i]); } // Repaint the scene view if necessary if (!Application.isPlaying || EditorApplication.isPaused) { SceneView.RepaintAll(); } } }
static private CoroutineRet _waitInput(axis[] arr, Actions action) { int idx = action.idx(); bool done = false; while (!done) { /* Wait until the end of the next frame */ yield return(null); if (waitCaller.lastKey != KeyCode.None) { arr[idx] = new axis(waitCaller.lastKey); done = true; break; } else { /* Test every option in every gamepad :grimacing: */ for (int gpIdx = 1; !done && gpIdx < gamepadNum; gpIdx++) { for (int gpAxis = 0; gpAxis < gamepadAxisNum; gpAxis++) { string name = $"joystick {gpIdx} axis {gpAxis}"; int i = gpIdx * gamepadAxisNum + gpAxis; float rest = Input.axisRest[i]; float val = DefInput.GetAxisRaw(name); float diff = val - rest; /* Check that the axis is 80% of the way pressed * in the given direction */ if (val > rest && diff > 0.25f && diff / (1.0f - rest) >= 0.8f) { arr[idx] = new axis(name, axisType.positiveAxis, rest); done = true; break; } else if (val < rest && diff < -0.25f && Math.Abs(diff / (1.0f + rest)) >= 0.8f) { arr[idx] = new axis(name, axisType.negativeAxis, rest); done = true; break; } } for (int gpBt = 0; gpBt < gamepadButtonNum; gpBt++) { string name = $"joystick {gpIdx} button {gpBt}"; if (DefInput.GetButton(name)) { arr[idx] = new axis(name, axisType.none); done = true; break; } } } } } waitFunc = null; waitCaller.GetComponentInChildren <KeyLogger>().enabled = false; waitCaller = null; }
public override void OnInspectorGUI(NavGraph target) { var graph = target as RecastGraph; bool preEnabled = GUI.enabled; System.Int64 estWidth = Mathf.RoundToInt(Mathf.Ceil(graph.forcedBoundsSize.x / graph.cellSize)); System.Int64 estDepth = Mathf.RoundToInt(Mathf.Ceil(graph.forcedBoundsSize.z / graph.cellSize)); // Show a warning if the number of voxels is too large if (estWidth * estDepth >= 1024 * 1024 || estDepth >= 1024 * 1024 || estWidth >= 1024 * 1024) { GUIStyle helpBox = GUI.skin.FindStyle("HelpBox") ?? GUI.skin.FindStyle("Box"); Color preColor = GUI.color; if (estWidth * estDepth >= 2048 * 2048 || estDepth >= 2048 * 2048 || estWidth >= 2048 * 2048) { GUI.color = Color.red; } else { GUI.color = Color.yellow; } GUILayout.Label("Warning : Might take some time to calculate", helpBox); GUI.color = preColor; } GUI.enabled = false; EditorGUILayout.LabelField(new GUIContent("Width (voxels)", "Based on the cell size and the bounding box"), new GUIContent(estWidth.ToString())); EditorGUILayout.LabelField(new GUIContent("Depth (voxels)", "Based on the cell size and the bounding box"), new GUIContent(estDepth.ToString())); GUI.enabled = preEnabled; graph.cellSize = EditorGUILayout.FloatField(new GUIContent("Cell Size", "Size of one voxel in world units"), graph.cellSize); if (graph.cellSize < 0.001F) { graph.cellSize = 0.001F; } graph.useTiles = (UseTiles)EditorGUILayout.EnumPopup("Use Tiles", graph.useTiles ? UseTiles.UseTiles : UseTiles.DontUseTiles) == UseTiles.UseTiles; if (graph.useTiles) { EditorGUI.indentLevel++; graph.editorTileSize = EditorGUILayout.IntField(new GUIContent("Tile Size", "Size in voxels of a single tile.\n" + "This is the width of the tile.\n" + "\n" + "A large tile size can be faster to initially scan (but beware of out of memory issues if you try with a too large tile size in a large world)\n" + "smaller tile sizes are (much) faster to update.\n" + "\n" + "Different tile sizes can affect the quality of paths. It is often good to split up huge open areas into several tiles for\n" + "better quality paths, but too small tiles can lead to effects looking like invisible obstacles."), graph.editorTileSize); EditorGUI.indentLevel--; } graph.minRegionSize = EditorGUILayout.FloatField(new GUIContent("Min Region Size", "Small regions will be removed. In square world units"), graph.minRegionSize); graph.walkableHeight = EditorGUILayout.FloatField(new GUIContent("Walkable Height", "Minimum distance to the roof for an area to be walkable"), graph.walkableHeight); graph.walkableHeight = Mathf.Max(graph.walkableHeight, 0); graph.walkableClimb = EditorGUILayout.FloatField(new GUIContent("Walkable Climb", "How high can the character climb"), graph.walkableClimb); // A walkableClimb higher than this can cause issues when generating the navmesh since then it can in some cases // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with a navmesh without links) if (graph.walkableClimb >= graph.walkableHeight) { graph.walkableClimb = graph.walkableHeight; EditorGUILayout.HelpBox("Walkable climb should be less than walkable height. Clamping to " + graph.walkableHeight + ".", MessageType.Warning); } else if (graph.walkableClimb < 0) { graph.walkableClimb = 0; } graph.characterRadius = EditorGUILayout.FloatField(new GUIContent("Character Radius", "Radius of the character. It's good to add some margin.\nIn world units."), graph.characterRadius); graph.characterRadius = Mathf.Max(graph.characterRadius, 0); if (graph.characterRadius < graph.cellSize * 2) { EditorGUILayout.HelpBox("For best navmesh quality, it is recommended to keep the character radius at least 2 times as large as the cell size. Smaller cell sizes will give you higher quality navmeshes, but it will take more time to scan the graph.", MessageType.Warning); } graph.maxSlope = EditorGUILayout.Slider(new GUIContent("Max Slope", "Approximate maximum slope"), graph.maxSlope, 0F, 90F); graph.maxEdgeLength = EditorGUILayout.FloatField(new GUIContent("Max Border Edge Length", "Maximum length of one border edge in the completed navmesh before it is split. A lower value can often yield better quality graphs, but don't use so low values so that you get a lot of thin triangles."), graph.maxEdgeLength); graph.maxEdgeLength = graph.maxEdgeLength < graph.cellSize ? graph.cellSize : graph.maxEdgeLength; graph.contourMaxError = EditorGUILayout.FloatField(new GUIContent("Max Edge Error", "Amount of simplification to apply to edges.\nIn world units."), graph.contourMaxError); graph.rasterizeTerrain = EditorGUILayout.Toggle(new GUIContent("Rasterize Terrain", "Should a rasterized terrain be included"), graph.rasterizeTerrain); if (graph.rasterizeTerrain) { EditorGUI.indentLevel++; graph.rasterizeTrees = EditorGUILayout.Toggle(new GUIContent("Rasterize Trees", "Rasterize tree colliders on terrains. " + "If the tree prefab has a collider, that collider will be rasterized. " + "Otherwise a simple box collider will be used and the script will " + "try to adjust it to the tree's scale, it might not do a very good job though so " + "an attached collider is preferable."), graph.rasterizeTrees); if (graph.rasterizeTrees) { EditorGUI.indentLevel++; graph.colliderRasterizeDetail = EditorGUILayout.FloatField(new GUIContent("Collider Detail", "Controls the detail of the generated collider meshes. " + "Increasing does not necessarily yield better navmeshes, but lowering will speed up scan.\n" + "Spheres and capsule colliders will be converted to meshes in order to be able to rasterize them, a higher value will increase the number of triangles in those meshes."), graph.colliderRasterizeDetail); EditorGUI.indentLevel--; } graph.terrainSampleSize = EditorGUILayout.IntField(new GUIContent("Terrain Sample Size", "Size of terrain samples. A lower value is better, but slower"), graph.terrainSampleSize); graph.terrainSampleSize = graph.terrainSampleSize < 1 ? 1 : graph.terrainSampleSize; //Clamp to at least 1 EditorGUI.indentLevel--; } graph.rasterizeMeshes = EditorGUILayout.Toggle(new GUIContent("Rasterize Meshes", "Should meshes be rasterized and used for building the navmesh"), graph.rasterizeMeshes); graph.rasterizeColliders = EditorGUILayout.Toggle(new GUIContent("Rasterize Colliders", "Should colliders be rasterized and used for building the navmesh"), graph.rasterizeColliders); if (graph.rasterizeColliders) { EditorGUI.indentLevel++; graph.colliderRasterizeDetail = EditorGUILayout.FloatField(new GUIContent("Collider Detail", "Controls the detail of the generated collider meshes. " + "Increasing does not necessarily yield better navmeshes, but lowering will speed up scan.\n" + "Spheres and capsule colliders will be converted to meshes in order to be able to rasterize them, a higher value will increase the number of triangles in those meshes."), graph.colliderRasterizeDetail); EditorGUI.indentLevel--; } if (graph.rasterizeMeshes && graph.rasterizeColliders) { EditorGUILayout.HelpBox("You are rasterizing both meshes and colliders, this might just be duplicating the work that is done if the colliders and meshes are similar in shape. You can use the RecastMeshObj component" + " to always include some specific objects regardless of what the above settings are set to.", MessageType.Info); } Separator(); graph.forcedBoundsCenter = EditorGUILayout.Vector3Field("Center", graph.forcedBoundsCenter); graph.forcedBoundsSize = EditorGUILayout.Vector3Field("Size", graph.forcedBoundsSize); // Make sure the bounding box is not infinitely thin along any axis graph.forcedBoundsSize = Vector3.Max(graph.forcedBoundsSize, Vector3.one * 0.001f); graph.rotation = EditorGUILayout.Vector3Field("Rotation", graph.rotation); if (GUILayout.Button(new GUIContent("Snap bounds to scene", "Will snap the bounds of the graph to exactly contain all meshes that the bounds currently touches"))) { graph.SnapForceBoundsToScene(); GUI.changed = true; } Separator(); EditorGUILayout.HelpBox("Objects contained in any of these masks will be rasterized", MessageType.None); graph.mask = EditorGUILayoutx.LayerMaskField("Layer Mask", graph.mask); tagMaskFoldout = EditorGUILayoutx.UnityTagMaskList(new GUIContent("Tag Mask"), tagMaskFoldout, graph.tagMask); Separator(); GUILayout.BeginHorizontal(); GUILayout.Space(18); graph.showMeshSurface = GUILayout.Toggle(graph.showMeshSurface, new GUIContent("Show surface", "Toggles gizmos for drawing the surface of the mesh"), EditorStyles.miniButtonLeft); graph.showMeshOutline = GUILayout.Toggle(graph.showMeshOutline, new GUIContent("Show outline", "Toggles gizmos for drawing an outline of the nodes"), EditorStyles.miniButtonMid); graph.showNodeConnections = GUILayout.Toggle(graph.showNodeConnections, new GUIContent("Show connections", "Toggles gizmos for drawing node connections"), EditorStyles.miniButtonRight); GUILayout.EndHorizontal(); Separator(); GUILayout.Label(new GUIContent("Advanced"), EditorStyles.boldLabel); if (GUILayout.Button("Export to .obj file")) { ExportToFile(graph); } graph.relevantGraphSurfaceMode = (RecastGraph.RelevantGraphSurfaceMode)EditorGUILayout.EnumPopup(new GUIContent("Relevant Graph Surface Mode", "Require every region to have a RelevantGraphSurface component inside it.\n" + "A RelevantGraphSurface component placed in the scene specifies that\n" + "the navmesh region it is inside should be included in the navmesh.\n\n" + "If this is set to OnlyForCompletelyInsideTile\n" + "a navmesh region is included in the navmesh if it\n" + "has a RelevantGraphSurface inside it, or if it\n" + "is adjacent to a tile border. This can leave some small regions\n" + "which you didn't want to have included because they are adjacent\n" + "to tile borders, but it removes the need to place a component\n" + "in every single tile, which can be tedious (see below).\n\n" + "If this is set to RequireForAll\n" + "a navmesh region is included only if it has a RelevantGraphSurface\n" + "inside it. Note that even though the navmesh\n" + "looks continous between tiles, the tiles are computed individually\n" + "and therefore you need a RelevantGraphSurface component for each\n" + "region and for each tile."), graph.relevantGraphSurfaceMode); graph.nearestSearchOnlyXZ = EditorGUILayout.Toggle(new GUIContent("Nearest node queries in XZ space", "Recomended for single-layered environments.\nFaster but can be inacurate esp. in multilayered contexts."), graph.nearestSearchOnlyXZ); if (graph.nearestSearchOnlyXZ && (Mathf.Abs(graph.rotation.x) > 1 || Mathf.Abs(graph.rotation.z) > 1)) { EditorGUILayout.HelpBox("Nearest node queries in XZ space is not recommended for rotated graphs since XZ space no longer corresponds to the ground plane", MessageType.Warning); } }
// Returns the angle in degrees between two rotations /a/ and /b/. public static float Angle(Quaternion a, Quaternion b) { float dot = Dot(a, b); return IsEqualUsingDot(dot) ? 0.0f : Mathf.Acos(Mathf.Min(Mathf.Abs(dot), 1.0F)) * 2.0F * Mathf.Rad2Deg; }
/** Creates a VO for avoiding another agent. * \param center The position of the other agent relative to this agent. * \param offset Offset of the velocity obstacle. For example to account for the agents' relative velocities. * \param radius Combined radius of the two agents (radius1 + radius2). * \param inverseDt 1 divided by the local avoidance time horizon (e.g avoid agents that we will hit within the next 2 seconds). * \param inverseDeltaTime 1 divided by the time step length. */ public VO(Vector2 center, Vector2 offset, float radius, float inverseDt, float inverseDeltaTime) { // Adjusted so that a parameter weightFactor of 1 will be the default ("natural") weight factor this.weightFactor = 1; weightBonus = 0; //this.radius = radius; Vector2 globalCenter; circleCenter = center * inverseDt + offset; this.weightFactor = 4 * Mathf.Exp(-Sqr(center.sqrMagnitude / (radius * radius))) + 1; // Collision? if (center.magnitude < radius) { colliding = true; // 0.001 is there to make sure lin1.magnitude is not so small that the normalization // below will return Vector2.zero as that will make the VO invalid and it will be ignored. line1 = center.normalized * (center.magnitude - radius - 0.001f) * 0.3f * inverseDeltaTime; dir1 = new Vector2(line1.y, -line1.x).normalized; line1 += offset; cutoffDir = Vector2.zero; cutoffLine = Vector2.zero; dir2 = Vector2.zero; line2 = Vector2.zero; this.radius = 0; } else { colliding = false; center *= inverseDt; radius *= inverseDt; globalCenter = center + offset; // 0.001 is there to make sure cutoffDistance is not so small that the normalization // below will return Vector2.zero as that will make the VO invalid and it will be ignored. var cutoffDistance = center.magnitude - radius + 0.001f; cutoffLine = center.normalized * cutoffDistance; cutoffDir = new Vector2(-cutoffLine.y, cutoffLine.x).normalized; cutoffLine += offset; float alpha = Mathf.Atan2(-center.y, -center.x); float delta = Mathf.Abs(Mathf.Acos(radius / center.magnitude)); this.radius = radius; // Bounding Lines // Point on circle line1 = new Vector2(Mathf.Cos(alpha + delta), Mathf.Sin(alpha + delta)); // Vector tangent to circle which is the correct line tangent // Note that this vector is normalized dir1 = new Vector2(line1.y, -line1.x); // Point on circle line2 = new Vector2(Mathf.Cos(alpha - delta), Mathf.Sin(alpha - delta)); // Vector tangent to circle which is the correct line tangent // Note that this vector is normalized dir2 = new Vector2(line2.y, -line2.x); line1 = line1 * radius + globalCenter; line2 = line2 * radius + globalCenter; } segmentStart = Vector2.zero; segmentEnd = Vector2.zero; segment = false; }
public static Vector2 Abs(this Vector2 input) { return(new Vector2(Mathf.Abs(input.x), Mathf.Abs(input.y))); }