Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- [RequireComponent(
- typeof(Collider2D),
- typeof(Animator),
- typeof(Canvas))]
- public class SpeechBubble : MonoBehaviour, IPointerClickHandler
- {
- static List<SpeechBubble> activeSpeechBubbles;
- const int collisionSolverMaxSteps = 100;
- const int rotationSteps = 18;
- const float alpha = 0.99f;
- const float minDist = 0.01f;
- const float maxForce = 30.0f;
- const float G = 10.0f;
- float timeoutTime;
- float currentTimeoutTime;
- bool canTimeOut
- {
- get { return timeoutTime > 0.0f; }
- }
- Action onHide;
- Action onClick;
- Collider2D col2D;
- Animator animator;
- Vector3 destination;
- TextMeshProUGUI textfield
- {
- get
- {
- if (_textfield == null)
- _textfield = GetComponentInChildren<TextMeshProUGUI>();
- return _textfield;
- }
- }
- TextMeshProUGUI _textfield;
- string text
- {
- get
- {
- return _text;
- }
- set
- {
- _text = value;
- textfield.text = value;
- }
- }
- string _text;
- public Transform target;
- float lastAlpha;
- [SerializeField] float collisionSolverStepSize = 1.0f;
- [SerializeField] protected Vector2 defaultOffsetToTargetObject = new Vector2(-5.0f, 2.5f);
- private void OnEnable()
- {
- if (activeSpeechBubbles == null)
- activeSpeechBubbles = new List<SpeechBubble>();
- activeSpeechBubbles.Add(this);
- col2D = GetComponent<Collider2D>();
- animator = GetComponent<Animator>();
- lastAlpha = 1.0f;
- destination = transform.position;
- SolveSpeechBubbleOverlaps(this);
- animator.Play("Enter");
- }
- private void OnDisable()
- {
- activeSpeechBubbles?.Remove(this);
- onHide?.Invoke();
- }
- private void Update()
- {
- transform.position = Vector3.Lerp(transform.position, destination, 0.05f);
- transform.position = Vector3.MoveTowards(transform.position, destination, Time.deltaTime);
- }
- public void OnPointerClick(PointerEventData eventData)
- {
- onClick?.Invoke();
- }
- //paul pc: ~25 speech bubbles drop below 60 fps (for one frame when creating the speech bubble)
- private static void SolveSpeechBubbleOverlaps(SpeechBubble ogBubble)
- {
- UIController.instance.StartCoroutine(SolveStuff());
- }
- static IEnumerator SolveStuff()
- {
- yield return null; //must wait for one frame so the scene graph updates with the instantiated collider!
- Dictionary<SpeechBubble, Vector3> ogPositions = new Dictionary<SpeechBubble, Vector3>();
- Physics2D.autoSyncTransforms = false;
- foreach (SpeechBubble speechBubble in activeSpeechBubbles)
- {
- ogPositions.Add(speechBubble, speechBubble.transform.position);
- }
- for (int i = 0; i < collisionSolverMaxSteps; i++)
- {
- foreach (SpeechBubble speechBubble in activeSpeechBubbles)
- {
- Vector3 force = ComputeForce(speechBubble);
- speechBubble.transform.Translate(force * speechBubble.lastAlpha);
- speechBubble.lastAlpha *= alpha;
- }
- Physics2D.SyncTransforms();
- }
- Physics2D.autoSyncTransforms = true;
- foreach (SpeechBubble speechBubble in ogPositions.Keys)
- {
- speechBubble.destination = speechBubble.transform.position;
- speechBubble.transform.position = ogPositions[speechBubble];
- }
- }
- private static Vector3 ComputeForce(SpeechBubble speechBubble)
- {
- Vector3 aggForce = Vector3.zero;
- Bounds bounds = speechBubble.col2D.bounds;
- int mask = ~LayerMask.GetMask("");
- ContactFilter2D cFilter = new ContactFilter2D();
- cFilter.layerMask = mask;
- //Collider2D[] overlaps = Physics2D.OverlapBoxAll(bounds.center, bounds.size, 0f, mask);
- List<Collider2D> overlaps = new List<Collider2D>();
- Physics2D.OverlapCollider(speechBubble.col2D, cFilter, overlaps);
- //each collider contributes to the force vector
- foreach (Collider2D col in overlaps)
- {
- if (col == null)
- continue;
- if (col == speechBubble.col2D)
- continue;
- Vector3 dir = speechBubble.transform.position - col.transform.position;
- float dist = dir.magnitude;
- dist = Mathf.Max(dist, minDist); //need epsilon to avoid dividing by 0
- if (dir == Vector3.zero)
- dir = UnityEngine.Random.insideUnitCircle;
- float den = (col.bounds.extents.magnitude * bounds.extents.magnitude);
- float force = G * den / (dist * dist);
- aggForce += dir.normalized * force;
- }
- //the target where the speech bubble wants to be also contributes to the force vector
- if (speechBubble.target != null)
- {
- Vector3 dirToTargetPoint = speechBubble.target.transform.position - speechBubble.transform.position;
- float forceToTargetPoint = dirToTargetPoint.magnitude;
- aggForce += dirToTargetPoint.normalized * forceToTargetPoint;
- }
- aggForce /= overlaps.Count + 1;
- Vector3 output = aggForce;
- Vector3 boundsOG = bounds.center;
- for (int i = 0; i < rotationSteps; i++)
- {
- output = Vector3.zero;
- aggForce = Quaternion.AngleAxis(360.0f / rotationSteps * i, Vector3.back) * aggForce;
- RaycastHit2D[] hit = Physics2D.RaycastAll(
- speechBubble.transform.position,
- aggForce.normalized,
- aggForce.magnitude);
- if (hit.Length <= 0 ||
- hit.All(h => h.fraction == 0))
- {
- output = aggForce;
- break;
- }
- }
- return Vector3.ClampMagnitude(output, maxForce);
- }
- public static SpeechBubble Show(
- string text,
- GameObject target,
- Action onHide,
- Action onClick = null,
- float timeoutTime = 0.0f)
- {
- SpeechBubble inst = Instantiate(UIController.instance.speechBubble);
- inst.col2D = inst.GetComponent<Collider2D>();
- Physics2D.SyncTransforms();
- inst.timeoutTime = 0.0f;
- inst.currentTimeoutTime = 0.0f;
- inst.onHide = onHide;
- inst.onClick = onClick;
- inst.text = text;
- inst.GetComponent<Canvas>().worldCamera = UIController.instance.worldUICamera;
- if (target != null)
- {
- inst.target = target.transform;
- inst.transform.position =
- target.transform.position +
- (Vector3)inst.defaultOffsetToTargetObject;
- }
- return inst;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement