/// <summary> /// Inspector - Finds the collision points between an arc extrapolated to be distance long (the PlanetariaRaycastHit structs have no particular order) /// </summary> /// <param name="arc">A fragment that defines the arc in space (might not be fully used or return collisions after the end of the arc).</param> /// <param name="distance">The distance to raycast (may be greater than or less than the length of the arc - or negative).</param> /// <param name="layer_mask">The collision mask that defines which objects will be ignored.</param> /// <returns>All of the collision points of the Raycast (listed exactly once).</returns> private static PlanetariaRaycastHit[] unordered_raycast_all(Arc arc, float distance, int layer_mask, bool collide_with_fields) { //float angle = arc.angle(); // this would determine the intersections for the un-modified arc (ignoring distance) // desired_angle = desired_length * (partial_angle/partial_length) i.e. length * length_to_angle ratio float desired_angle = distance * (arc.angle() / arc.length()); // TODO: verify negative distances go backwards desired_angle = Mathf.Clamp(desired_angle, -2 * Mathf.PI, 2 * Mathf.PI); // primative arc points Vector3 arc_left = arc.position(-arc.angle() / 2); Vector3 arc_center = arc.position(-arc.angle() / 2 + desired_angle / 2); Vector3 arc_right = arc.position(-arc.angle() / 2 + desired_angle); SerializedArc ray_arc = ArcFactory.curve(arc_left, arc_center, arc_right); PlanetariaShape ray_shape = PlanetariaShape.Create(new List <SerializedArc> { ray_arc }, false); // composites Vector3 arc_boundary_midpoint = (arc_left + arc_right) / 2; // if the arc is like a wooden bow, this is the midpoint of the string Vector3 arc_forward = (arc_center - arc_boundary_midpoint).normalized; // the direction a hypothetical arrow would travel Vector3 arc_up = arc.floor().normal; // orthogonal/perpendicular to the imaginary "bow" // UnityEngine.Physics.OverlapBox() requirements // FIXME: OPTIMIZE: half_extents currently provides unnecessary false positives because the "width" of plane (the depth into the distance and zero height are fine) Vector3 half_extents = new Vector3(1, 0, 1); // The largest collision "box" for a unit sphere is a radius of 1 in the x-z plane; height along y is 0. Vector3 center = arc_boundary_midpoint + arc_forward * 1; // The center of the "box" must be offset 1 (the radius) along the forward axis from the two arc boundaries. Quaternion rotation = Quaternion.LookRotation(arc_forward, arc_up); // SphereColliders (only) that represent potential collisions (not guaranteed). Collider[] colliders = Physics.OverlapBox(center, half_extents, rotation, layer_mask, QueryTriggerInteraction.Collide); // TODO: verify this casts properly List <PlanetariaRaycastHit> raycast_hits = new List <PlanetariaRaycastHit>(); Debug.Log(colliders.Length); foreach (SphereCollider sphere_collider in colliders) { PlanetariaCollider planetaria_collider = PlanetariaCache.collider_fetch(sphere_collider); if (planetaria_collider.is_field && !collide_with_fields) { Debug.LogError("Why?"); continue; } Quaternion geometry_rotation = planetaria_collider.gameObject.internal_game_object.transform.rotation; Debug.Log("Found a collider with " + planetaria_collider.shape.Length + " arcs."); foreach (Arc geometry_arc in ray_shape.block_collision(planetaria_collider.shape, geometry_rotation)) { Vector3[] intersections = PlanetariaIntersection.raycast_intersection(arc, geometry_arc, distance, geometry_rotation); // TODO: verify distance is indeed the angle in this scenario Debug.Log("Found an arc with " + intersections.Length + " intersections."); foreach (Vector3 intersection in intersections) { PlanetariaRaycastHit single_collision = PlanetariaRaycastHit.hit(arc, planetaria_collider, geometry_arc, intersection, distance); raycast_hits.Add(single_collision); } } } return(raycast_hits.ToArray()); }
private void OnTriggerStay(Collider collider) { optional <SphereCollider> sphere_collider = collider as SphereCollider; if (!sphere_collider.exists) { Debug.LogError("This should never happen"); return; } optional <PlanetariaCollider> other_collider = PlanetariaCache.collider_fetch(sphere_collider.data); if (!other_collider.exists) { Debug.LogError("This should never happen"); return; } Quaternion shift_from_self_to_other = other_collider.data.internal_transform.rotation; if (this.internal_transform.rotation != Quaternion.identity) // Only shift orientation when necessary { // TODO: verify the order of operations is correct (and logic itself) shift_from_self_to_other = Quaternion.Inverse(this.internal_transform.rotation) * shift_from_self_to_other; } if (other_collider.data.is_field) // field collision { if (this.shape.field_collision(other_collider.data.shape, shift_from_self_to_other)) { observer.potential_field_collision(other_collider.data); // TODO: augment field (like Unity triggers) works on both the sender and receiver. } } else // block collision { foreach (Arc intersection in this.shape.block_collision(other_collider.data.shape, shift_from_self_to_other)) { Vector3 position = planetaria_transform.position; if (other_collider.data.gameObject.internal_game_object.transform.rotation != Quaternion.identity) // Only shift orientation when necessary { position = Quaternion.Inverse(other_collider.data.gameObject.internal_game_object.transform.rotation) * position; } if (intersection.contains(position, planetaria_transform.scale / 2)) { observer.potential_block_collision(intersection, other_collider.data); // block collisions are handled in OnCollisionStay(): notification stage } } } }
private void cache(PlanetariaShape shape) { PlanetariaCache.uncache(this); shape_variable = shape; if (shape != null) { PlanetariaCache.cache(this); PlanetariaSphereCollider sphere = shape.bounding_sphere; internal_collider.center = sphere.center; internal_collider.radius = sphere.radius; } else { internal_collider.radius = float.NegativeInfinity; // FIXME: HACK: ensure there are no collisions } internal_collider.isTrigger = true; // Rigidbody must be added for collisions to be detected. }
protected override void OnDestroy() { PlanetariaCache.uncache(this); GameObject.Destroy(internal_collider); }