3 - Movimentação Point and Click + Obstáculos
Link para o repositório no github!!
O processo de fazer a mecânica de desviar dos obstáculos seguiu os seguintes passos:
- verificar se há um obstáculo entre a posição do player e o destino;
- criar 2 pontos de desvio; verificar qual lado está livre e é o mais curto;
- mais uma verificação entre o desvio e o destino final;
- extra: cortando caminho;
1. Para verificar se há obstáculos foi usado um RaycastHit2D[ ] e uma layer chamada "Obstacle". Neste caso, a direction e a distance se baseiam entre na posição do player e o destino final. Após a verificação é usado um foreach, pois pode haver mais de um obstáculo pelo caminho:
obs: precisa colocar a layer Obstacle nos objetos que precisam ser desviados na cena; isMoving = true é colocado após a verificaçao do foreach;
[SerializeField] private LayerMask obstacleLayer; . . . if (hitGround.collider != null){ destination = hitGround.point; RaycastHit2D[] obstaclesHit = Physics2D.RaycastAll(transform.position, destination - (Vector2)transform.position, Vector2.Distance(transform.position, destination), obstacleLayer); foreach (var item in obstaclesHit){ if (item.collider != null) . . . } isMoving = true; }
2. Para criar um ponto de desvio é necessário criar um bool para "caso estiver desviando" e um Vector2 para o ponto:
private Vector2 detourPosition; private bool isDetouring; void Start(){ isMoving = false; isDetouring = false; }
No método OnClick, dentro do foreach, a lógica do cálculo para o desvio seria:
- usar o tamanho do collider do obstáculo para conseguir um float de distância razoável;
if (item.collider != null){ Collider2D obsCollider = item.collider; Vector2 obsSize = obsCollider.bounds.size; float detourDistance = Mathf.Max(0.5f, Mathf.Min(obsSize.x, obsSize.y) / 2f);
- conseguir o ponto perpendicular (90 graus);
Vector2 direction = (destination - (Vector2)transform.position).normalized; Vector2 perpDirection = Vector2.Perpendicular(direction);
- fazer o cálculo verificando os dois lados do obstáculo; criar dois bool para verificar quais lados estão livres para passagem;
ponto do collider que o RaycastHit bate + ou - o ponto perpendicular X float de distância do collider
detourPos1 = item.point + perpDirection * detourDistance; detourPos2 = item.point - perpDirection * detourDistance; bool detour1Clear = !Physics2D.Raycast(playerPosition, detourPos1 - playerPosition, Vector2.Distance(playerPosition, detourPos1), obstacleLayer); bool detour2Clear = !Physics2D.Raycast(playerPosition, detourPos2 - playerPosition, Vector2.Distance(playerPosition, detourPos2), obstacleLayer);
- verificar qual lado está livre e mais perto;
if (detour1Clear && detour2Clear){ detourPosition = Vector2.Distance(playerPosition, detourPos1) < Vector2.Distance(playerPosition, detourPos2) ? detourPos1 : detourPos2; }else if (detour1Clear){ detourPosition = detourPos1; }else if (detour2Clear){ detourPosition = detourPos2; }else{ isMoving = false; return; } isDetouring = true; }
No Update:
if (isMoving){ Vector2 nextPosition = isDetouring ? detourPosition : destination; transform.position = Vector2.MoveTowards(transform.position, nextPosition, speed * Time.deltaTime); if ((Vector2)transform.position == destination){ if (isDetouring) isDetouring = false; else isMoving = false; } }
3. O script até esse ponto já é funcional, mas fiz mais uma verificação para evitar qualquer colisão restante. As verificações foram colocadas num método à parte, uma coroutine, para fazer os cálculos até ter o ponto de desvio.
private void CheckObstacles(){ RaycastHit2D[] obstaclesHit = Physics2D.RaycastAll(transform.position, destination - (Vector2)transform.position, Vector2.Distance(transform.position, destination), obstacleLayer); foreach (var item in obstaclesHit){ if (item.collider != null){ Collider2D obsCollider = item.collider; Vector2 obsSize = obsCollider.bounds.size; float detourDistance = Mathf.Max(0.5f, Mathf.Min(obsSize.x, obsSize.y) / 2f); Vector2 direction = (destination - (Vector2)transform.position).normalized; Vector2 perpDirection = Vector2.Perpendicular(direction); StartCoroutine(PathOptionsCheck(item.point, perpDirection, detourDistance)); isDetouring = true; } } isMoving = true; }
Na IEnumerator PathOptionsCheck foi colocado, além das verificações já existentes, mais uma verificação contendo 2 bool. Caso os 2 lados não estejam livre é recomeçado a coroutine mas com uma variação no float detourDistance:
IEnumerator PathOptionsCheck(Vector2 itemPoint, Vector2 perpDirection, float detourDistance){ detourPos1 = itemPoint + perpDirection * detourDistance; detourPos2 = itemPoint - perpDirection * detourDistance; detour1Clear = RaycastVerification(transform.position, detourPos1, Color.red); detour2Clear = RaycastVerification(transform.position, detourPos2, Color.blue); end1Clear = RaycastVerification(detourPos1, destination, Color.red); end2Clear = RaycastVerification(detourPos2, destination, Color.blue); //escolher o desvio if (detour1Clear && detour2Clear){ if (end1Clear && end2Clear) detourPosition = Vector2.Distance(transform.position, detourPos1) < Vector2.Distance(transform.position, detourPos2) ? detourPos1 : detourPos2; else if (end1Clear) detourPosition = detourPos1; else if (end2Clear) detourPosition = detourPos2; } else if (detour1Clear && end1Clear) detourPosition = detourPos1; else if (detour2Clear && end2Clear) detourPosition = detourPos2; else // quando os 2 lados não dão certo { isMoving = false; StartCoroutine(PathOptionsCheck(itemPoint, perpDirection, detourDistance + 0.5f)); } yield break; }
O método RaycastVerification é usado para retornar se o lado verificado está livre ou não. Há um Debug.DrawLine para poder ver os caminhos criados quando for testar na unity:
private bool RaycastVerification(Vector2 origin, Vector2 direction, Color color){ bool pathClear = !Physics2D.Raycast(origin, direction - origin, Vector2.Distance(origin, direction), obstacleLayer); Debug.DrawLine(origin, direction, color, 2f); if (pathClear) return true; else return false; }
4. No Update é possivel fazer a movimentação cortar caminho: enquanto vai até o desvio é feito um Raycast para verificar se isso é possivel.
if (isMoving){ Vector2 nextPosition = isDetouring ? detourPosition : destination; transform.position = Vector2.MoveTowards(transform.position, nextPosition, speed * Time.deltaTime); if (isDetouring && !Physics2D.Raycast(transform.position, destination - (Vector2)transform.position, Vector2.Distance(transform.position, destination), obstacleLayer)){ nextPosition = destination; isDetouring = false; } if ((Vector2)transform.position == nextPosition){ if (isDetouring) isDetouring = false; else isMoving = false; } }
Souls' Memories
Status | In development |
Author | ViiKi |
Genre | Puzzle |
Tags | Ghosts, Narrative, Point & Click |
More posts
- 4 - Interagindo com ItensJul 25, 2024
- 2 - Movimentação Point and Click + new input systemJul 12, 2024
- 1 - Unity e git lfs (large file storage)Jul 10, 2024
Leave a comment
Log in with itch.io to leave a comment.