Criando um gancho para gatos no Unity. Parte 1

imagem

Os ganchos para gatos acrescentam mecânicas divertidas e interessantes ao jogo. Você pode usá-los para percorrer níveis, lutar em arenas e obter itens. Mas, apesar da aparente simplicidade, a física do gerenciamento de cordas e a criação de comportamentos realistas podem ser desafiadoras!

Na primeira parte deste tutorial, implementamos nosso próprio sistema gancho-gato bidimensional e aprendemos o seguinte:

  • Crie um sistema de mira.
  • Use o renderizador de linha e a junta de distância para criar a corda.
  • Ensinaremos a corda a envolver objetos do jogo.
  • Calcule o ângulo de giro na corda e adicione força nessa direção.

Nota : este tutorial é destinado a usuários avançados e experientes e não abrange tópicos como adição de componentes, criação de novos scripts GameObject e sintaxe C #. Se você precisar aprimorar suas habilidades no Unity, consulte nossos tutoriais Introdução ao Unity e Introdução ao script de unidade . Como DistanceJoint2D é usado neste tutorial, você também deve examinar as Articulações de Física no Unity 2D e só depois retornar a este tutorial.

Começando a trabalhar


Faça o download do rascunho deste tutorial e abra-o no editor do Unity. O Unity 2017.1 ou superior é necessário para a operação.

Abra a cena do jogo na pasta Scenes e veja por onde começar:


Por enquanto, temos um personagem de jogador simples (lesma) e pedras penduradas no ar.

Até agora, os componentes importantes do GameObject Player são o colisor de cápsulas e o corpo rígido, que permitem a interação com objetos físicos no nível. Além disso, um script de movimento simples ( PlayerMovement ) é anexado ao personagem, permitindo que ele deslize no chão e realize saltos simples.

Pressione o botão Play para iniciar o jogo e tente controlar o personagem. A e D movem-na para a esquerda / direita e, quando você pressiona a barra de espaço, ela salta. Tente não escorregar e cair do penhasco, senão você morrerá!


Já temos o básico de gerenciamento, mas o maior problema agora é a falta de ganchos para gatos.

Criando ganchos e cordas


A princípio, o sistema cat-hook parece bastante simples, mas para sua implementação de alta qualidade é necessário levar em consideração muitos aspectos. Aqui estão alguns dos requisitos para a mecânica bidimensional de gancho de gato:

  • Renderizador de linha para exibir a corda. Quando a corda envolve objetos, podemos adicionar mais segmentos ao renderizador de linhas e colocar os vértices nos pontos correspondentes às quebras da corda.
  • DistanceJoint2D. Ele pode ser usado para conectar o ponto de ancoragem atual do gancho de gato, para que nossa lesma possa balançar. Também nos permite ajustar a distância que pode ser usada para aumentar e reduzir o cabo.
  • Filho de GameObject com RigidBody2D, que pode ser movido dependendo da localização atual do ponto de ancoragem do gancho. Em essência, será o ponto de suspensão / âncora da corda.
  • Raycast para jogar um gancho e anexar a objetos.

Selecione o objeto Player na Hierarquia e adicione um novo GameObject filho chamado RopeHingeAnchor . Este GameObject será usado para posicionar o ponto de suspensão / ancoragem do gancho de gato.

Adicione os componentes SpriteRenderer e RigidBody2D ao RopeHingeAnchor .

Para SpriteRenderer, defina a propriedade Sprite para usar o valor UISprite e altere Order in Layer para 2 . Desative o componente desmarcando a caixa ao lado de seu nome.

Para o componente RigidBody2D, defina a propriedade Tipo de corpo como Cinemático . Este ponto será movido não pelo mecanismo físico, mas pelo código.

Selecione a camada Corda e defina os valores de escala X e Y do componente Transform como 4 .


Selecione Player novamente e conecte o novo componente DistanceJoint2D .

Arraste o RopeHingeAnchor da Hierarquia para a propriedade Corpo Rígido Conectado do componente DistanceJoint2D e desative a Distância de Configuração Automática .


Crie um novo script C # chamado RopeSystem na pasta do projeto Scripts e abra-o no editor de código.

Remova o método Update .

Na parte superior do script dentro da RopeSystem classe RopeSystem adicione novas variáveis, o método Awake() e o novo método Update :

 // 1 public GameObject ropeHingeAnchor; public DistanceJoint2D ropeJoint; public Transform crosshair; public SpriteRenderer crosshairSprite; public PlayerMovement playerMovement; private bool ropeAttached; private Vector2 playerPosition; private Rigidbody2D ropeHingeAnchorRb; private SpriteRenderer ropeHingeAnchorSprite; void Awake() { // 2 ropeJoint.enabled = false; playerPosition = transform.position; ropeHingeAnchorRb = ropeHingeAnchor.GetComponent<Rigidbody2D>(); ropeHingeAnchorSprite = ropeHingeAnchor.GetComponent<SpriteRenderer>(); } void Update() { // 3 var worldMousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0f)); var facingDirection = worldMousePosition - transform.position; var aimAngle = Mathf.Atan2(facingDirection.y, facingDirection.x); if (aimAngle < 0f) { aimAngle = Mathf.PI * 2 + aimAngle; } // 4 var aimDirection = Quaternion.Euler(0, 0, aimAngle * Mathf.Rad2Deg) * Vector2.right; // 5 playerPosition = transform.position; // 6 if (!ropeAttached) { } else { } } 

Vamos analisar cada parte em ordem:

  1. Usamos essas variáveis ​​para rastrear os vários componentes com os quais o script RopeSystem irá interagir.
  2. O método Awake inicia no início do jogo e desativa o ropeJoint (o componente DistanceJoint2D). Ele também define playerPosition na posição atual do Player.
  3. Essa é a parte mais importante do loop principal Update() . Primeiro, obtemos a posição do cursor do mouse no mundo usando o ScreenToWorldPoint câmera ScreenToWorldPoint . Em seguida, calculamos a direção do nosso olhar subtraindo a posição do jogador da posição do mouse no mundo. Em seguida, usamos para criar aimAngle , que é uma representação do ângulo de mira do cursor. O valor armazena um valor positivo na construção if.
  4. aimDirection é uma reviravolta que será útil mais tarde. Estamos interessados ​​apenas no valor Z, pois estamos usando uma câmera 2D, e este é o único SO correspondente. Passamos aimAngle * Mathf.Rad2Deg , que converte o ângulo do radiano em um ângulo em graus.
  5. A posição do jogador é monitorada usando uma variável conveniente que permite que você não se refira constantemente a transform.Position .
  6. Finalmente, temos a construção if..else , que usaremos em breve para determinar se a corda está presa ao ponto de ancoragem.

Salve o script e retorne ao editor.

Anexe o componente RopeSystem ao objeto Player e pendure os vários componentes nos campos públicos que criamos no script RopeSystem . Arraste Player , Crosshair e RopeHingeAnchor para os campos apropriados:

  • Âncora de dobradiça de corda : RopeHingeAnchor
  • Articulação de corda : Jogador
  • Crosshair : Crosshair
  • Sprite de mira: Crosshair
  • Movimento do Jogador: Player


Agora, estamos apenas fazendo todos esses cálculos complexos, mas até agora não há visualização que possa mostrá-los em ação. Mas não se preocupe, faremos isso em breve.

Abra o script RopeSystem e adicione um novo método a ele:

 private void SetCrosshairPosition(float aimAngle) { if (!crosshairSprite.enabled) { crosshairSprite.enabled = true; } var x = transform.position.x + 1f * Mathf.Cos(aimAngle); var y = transform.position.y + 1f * Mathf.Sin(aimAngle); var crossHairPosition = new Vector3(x, y, 0); crosshair.transform.position = crossHairPosition; } 

Este método posiciona a mira com base no aimAngle transmitido (o valor flutuante que calculamos em Update() ) para que ele gire em torno do player com um raio de 1 unidade. Também incluímos um escopo de sprite, caso ainda não esteja pronto.

Em Update() !ropeAttached a construção !ropeAttached para verificar !ropeAttached para que fique assim:

 if (!ropeAttached) { SetCrosshairPosition(aimAngle); } else { crosshairSprite.enabled = false; } 

Salve o script e execute o jogo. Agora nossa lesma deve ser capaz de mirar com uma visão.


A próxima peça de lógica que precisa ser implementada é a de um gancho de gato. Já determinamos a direção da mira, por isso precisamos de um método que a receba como parâmetro.

Adicione as seguintes variáveis ​​abaixo das variáveis ​​no script RopeSystem :

 public LineRenderer ropeRenderer; public LayerMask ropeLayerMask; private float ropeMaxCastDistance = 20f; private List<Vector2> ropePositions = new List<Vector2>(); 

O LineRenderer conterá um link para um renderizador de linha que desenha a corda. O LayerMask permite que você personalize as camadas físicas com as quais o gancho pode interagir. O valor ropeMaxCastDistance define a distância máxima que o raycast pode "disparar".

Finalmente, a lista de posições do Vector2 será usada para rastrear os pontos de enrolamento de corda, os quais discutiremos mais adiante.

Adicione os seguintes novos métodos:

 // 1 private void HandleInput(Vector2 aimDirection) { if (Input.GetMouseButton(0)) { // 2 if (ropeAttached) return; ropeRenderer.enabled = true; var hit = Physics2D.Raycast(playerPosition, aimDirection, ropeMaxCastDistance, ropeLayerMask); // 3 if (hit.collider != null) { ropeAttached = true; if (!ropePositions.Contains(hit.point)) { // 4 //    ,    -  . transform.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 2f), ForceMode2D.Impulse); ropePositions.Add(hit.point); ropeJoint.distance = Vector2.Distance(playerPosition, hit.point); ropeJoint.enabled = true; ropeHingeAnchorSprite.enabled = true; } } // 5 else { ropeRenderer.enabled = false; ropeAttached = false; ropeJoint.enabled = false; } } if (Input.GetMouseButton(1)) { ResetRope(); } } // 6 private void ResetRope() { ropeJoint.enabled = false; ropeAttached = false; playerMovement.isSwinging = false; ropeRenderer.positionCount = 2; ropeRenderer.SetPosition(0, transform.position); ropeRenderer.SetPosition(1, transform.position); ropePositions.Clear(); ropeHingeAnchorSprite.enabled = false; } 

Aqui está o que o código acima faz:

  1. HandleInput é chamado no loop Update() e simplesmente pesquisa a entrada dos botões esquerdo e direito do mouse.
  2. Quando um clique esquerdo é registrado, a linha da corda é ativada e um radiodifusão 2D é disparado da posição do jogador na direção da mira. A distância máxima é definida para que o gancho não possa ser disparado a uma distância infinita, e uma máscara é aplicada para que seja possível selecionar as camadas da física com as quais o raycast pode colidir.
  3. Se um hit de raycast for detectado, o ropeAttached será true e uma lista das posições dos vértices da corda será verificada para garantir que não haja nenhum ponto lá.
  4. Se o teste retornar verdadeiro, um pequeno impulso de força será adicionado à lesma para que ela salte acima do solo, a ropeJoint (DistanceJoint2D) é ativada, que é definida para a distância igual à distância entre a lesma e o ponto de impacto do raio. Um sprite de ponto de ancoragem também está incluído.
  5. Se o raycast não atingir nada, o renderizador de linha e o ropeJoint serão desativados e o sinalizador ropeAttached será falso.
  6. Se o botão direito do mouse for pressionado, o método ResetRope() é ResetRope() , que desativa e redefine todos os parâmetros relacionados à corda / gancho aos valores que deveriam ser se o gancho não for usado.

Na parte inferior do nosso método Update , adicione uma chamada ao novo método HandleInput() e passe o valor aimDirection para aimDirection :

 HandleInput(aimDirection); 

Salve as alterações em RopeSystem.cs e retorne ao editor.

Adicionando uma corda


Nossa lesma não será capaz de voar pelo ar sem uma corda; portanto, é hora de fornecer algo que seja uma representação visual da corda e que tenha a capacidade de "virar" nos cantos.

O renderizador de linha é ideal para isso, pois permite transferir o número de pontos e sua posição no espaço do mundo.

A idéia aqui é que sempre armazenemos o primeiro vértice da corda (0) na posição do jogador, e todos os outros vértices são posicionados dinamicamente quando a corda deve envolver algo, incluindo a posição atual da dobradiça, que é o próximo ponto ao longo da corda do jogador.

Selecione Player e adicione o componente LineRenderer a ele. Defina Largura como 0,075 . Expanda a lista Materiais e, como Elemento 0, selecione o material RopeMaterial localizado na pasta Materiais do projeto. Por fim, para Renderizador de linha, para Modo de textura, selecione Distribuir por segmento .


Arraste o componente Renderizador de linhas para o campo Renderizador de cordas do componente Sistema de cordas .

Clique na lista suspensa de Máscara de camada de corda e selecione como camadas com as quais o raycast Default, Rope e Pivot podem interagir. Devido a isso, ao “filmar” um raycast, ele colidirá apenas com essas camadas, mas não com outros objetos, como um jogador.


Se você iniciar o jogo agora, notará um comportamento estranho. Quando apontamos para uma pedra acima da cabeça da lesma e disparamos com um gancho, fazemos um pequeno salto para cima, após o qual nosso amigo começa a se comportar de maneira aleatória.


Ainda não definimos a distância para a junta de distância, além disso, os vértices da renderização da linha não estão configurados. Portanto, não vemos a corda e, como a junta de distância está diretamente acima da posição da lesma, o valor atual da distância da junta de distância a empurra para as pedras abaixo dela.

Mas não se preocupe, agora vamos resolver esse problema.

No script RopeSystem.cs, adicione um novo operador no início da classe:

 using System.Linq; 

Isso nos permite usar consultas LINQ, que no nosso caso simplesmente nos permitem encontrar convenientemente o primeiro ou o último elemento da lista ropePositions .

Nota : LINQ (Consulta Integrada à Linguagem) é o nome de um conjunto de tecnologias baseadas na incorporação de recursos de consulta diretamente em C #. Você pode ler mais sobre isso aqui .

Adicione uma nova variável privada bool chamada distanceSet sob as outras variáveis:

 private bool distanceSet; 

Usaremos essa variável como uma bandeira para que o script reconheça que a distância da corda (para o ponto entre o jogador e o ponto de referência atual em que o gancho de gato está conectado) está definida corretamente.

Agora adicione um novo método que usaremos para definir as posições dos vértices da corda para renderizar a linha e definir a distância da junta de distância na lista armazenada de posições com a corda ( ropePositions ):

 private void UpdateRopePositions() { // 1 if (!ropeAttached) { return; } // 2 ropeRenderer.positionCount = ropePositions.Count + 1; // 3 for (var i = ropeRenderer.positionCount - 1; i >= 0; i--) { if (i != ropeRenderer.positionCount - 1) // if not the Last point of line renderer { ropeRenderer.SetPosition(i, ropePositions[i]); // 4 if (i == ropePositions.Count - 1 || ropePositions.Count == 1) { var ropePosition = ropePositions[ropePositions.Count - 1]; if (ropePositions.Count == 1) { ropeHingeAnchorRb.transform.position = ropePosition; if (!distanceSet) { ropeJoint.distance = Vector2.Distance(transform.position, ropePosition); distanceSet = true; } } else { ropeHingeAnchorRb.transform.position = ropePosition; if (!distanceSet) { ropeJoint.distance = Vector2.Distance(transform.position, ropePosition); distanceSet = true; } } } // 5 else if (i - 1 == ropePositions.IndexOf(ropePositions.Last())) { var ropePosition = ropePositions.Last(); ropeHingeAnchorRb.transform.position = ropePosition; if (!distanceSet) { ropeJoint.distance = Vector2.Distance(transform.position, ropePosition); distanceSet = true; } } } else { // 6 ropeRenderer.SetPosition(i, transform.position); } } } 

Explique o código mostrado acima:

  1. Saia do método se a corda não estiver presa.
  2. Atribuímos o valor dos pontos de renderização da linha da corda ao número de posições armazenadas ropePositions da ropePositions , mais 1 a mais (para a posição do jogador).
  3. Fazemos um loop em torno da lista ropePositions e em ropePositions para cada posição (exceto a última), atribuímos a posição do vértice do renderizador de linha ao valor da posição Vector2 armazenada pelo índice de loop na lista ropePositions .
  4. Atribua ao ponto de ancoragem da corda o segundo da posição final da corda, na qual o atual ponto de dobradiça / âncora deve estar, ou se tivermos apenas uma posição da corda, faça dele o ponto de ancoragem. Então, definimos a distância da ropeJoint igual à distância entre o jogador e a posição atual da corda, que ropeJoint no loop.
  5. A construção if lida com o caso em que a posição atual da corda no loop é a segunda do final; ou seja, o ponto em que a corda se conecta ao objeto, ou seja ponto atual da dobradiça / âncora.
  6. Esse else bloco lida com a atribuição da posição do último vértice da corda ao valor da posição atual do jogador.

Lembre-se de adicionar a chamada UpdateRopePositions() no final de Update() UpdateRopePositions() :

 UpdateRopePositions(); 

Salve as alterações no script e execute o jogo novamente. Faça um “pequeno espaço” um pequeno salto, mirando e atirando em um gancho em uma pedra acima do personagem. Agora você pode apreciar os frutos de seu trabalho - a lesma balança calmamente sobre as pedras.


Agora você pode ir para a janela de cena, selecionar Player, usar a ferramenta Move (por padrão, a tecla W ) para movê-la e observar como os dois vértices da linha da corda renderizam seguem a posição do gancho e a posição do jogador para puxar a corda. Depois de liberarmos o player, o DistanceJoint2D calcula corretamente a distância e a lesma continuará balançando na dobradiça conectada.


Manipulação de pontos de quebra automática


Até agora, brincar com uma lesma balançando não é mais útil do que uma toalha que repele a água; portanto, precisamos definitivamente complementá-la.


A boa notícia é que o método recém-adicionado para processar posições de corda pode ser usado no futuro. Até agora, estamos usando apenas duas posições de corda. Um é conectado à posição do jogador e o segundo à posição atual do ponto de ancoragem do gancho ao atirar nele.

O único problema é que, enquanto não rastreamos todas as posições potenciais da corda, isso precisa de um pouco de trabalho.

Para reconhecer as posições nas pedras em torno das quais a corda deve ser enrolada, adicionando uma nova posição de vértice ao renderizador de linhas, precisamos de um sistema que determine se o ponto de vértice do colisor está entre a linha reta entre a posição atual da lesma e o atual ponto de dobradiça / âncora da corda.

Parece que isso é trabalho novamente para o bom e velho raycast!


Primeiro, precisamos criar um método que possa encontrar o ponto mais próximo no colisor com base no ponto de impacto do raycast e nas bordas do colisor.

Adicione um novo método ao script RopeSystem.cs :

 // 1 private Vector2 GetClosestColliderPointFromRaycastHit(RaycastHit2D hit, PolygonCollider2D polyCollider) { // 2 var distanceDictionary = polyCollider.points.ToDictionary<Vector2, float, Vector2>( position => Vector2.Distance(hit.point, polyCollider.transform.TransformPoint(position)), position => polyCollider.transform.TransformPoint(position)); // 3 var orderedDictionary = distanceDictionary.OrderBy(e => e.Key); return orderedDictionary.Any() ? orderedDictionary.First().Value : Vector2.zero; } 

Se você não estiver familiarizado com as consultas LINQ, esse código pode parecer algum tipo de mágica em C # complicada.


Se sim, então não tenha medo. O LINQ trabalha muito para nós:

  1. Este método usa dois parâmetros - o objeto RaycastHit2D e o objeto PolygonCollider2D . Todas as pedras no nível têm coletores PolygonCollider2D; portanto, se sempre usarmos formas PolygonCollider2D, funcionará bem.
  2. É aqui que a mágica da consulta LINQ começa! Aqui, transformamos a coleção de pontos do colisor poligonal em um dicionário de posições Vector2 (o valor de cada elemento do dicionário é a própria posição), e a chave de cada elemento recebe o valor da distância desse ponto no jogador de posição do jogador (valor flutuante). Às vezes, algo mais acontece aqui: a posição resultante é convertida em espaço do mundo (por padrão, as posições dos vértices do colisor são armazenadas no espaço local, isto é, local em relação ao objeto ao qual o colisor pertence e precisamos de posições no espaço do mundo).
  3. O dicionário é classificado por chave. Em outras palavras, pela distância mais próxima da posição atual do jogador. A distância mais próxima é retornada, ou seja, qualquer ponto retornado por esse método é o ponto de colisão entre o jogador e o ponto atual da dobradiça do cabo!

Vamos voltar ao script RopeSystem.cs e adicionar uma nova variável de campo privado na parte superior:

 private Dictionary<Vector2, int> wrapPointsLookup = new Dictionary<Vector2, int>(); 

Vamos usá-lo para rastrear as posições em torno das quais a corda pode enrolar.

No final do método Update() , localize a construção else que contém crosshairSprite.enabled = false; e adicione o seguinte:

 // 1 if (ropePositions.Count > 0) { // 2 var lastRopePoint = ropePositions.Last(); var playerToCurrentNextHit = Physics2D.Raycast(playerPosition, (lastRopePoint - playerPosition).normalized, Vector2.Distance(playerPosition, lastRopePoint) - 0.1f, ropeLayerMask); // 3 if (playerToCurrentNextHit) { var colliderWithVertices = playerToCurrentNextHit.collider as PolygonCollider2D; if (colliderWithVertices != null) { var closestPointToHit = GetClosestColliderPointFromRaycastHit(playerToCurrentNextHit, colliderWithVertices); // 4 if (wrapPointsLookup.ContainsKey(closestPointToHit)) { ResetRope(); return; } // 5 ropePositions.Add(closestPointToHit); wrapPointsLookup.Add(closestPointToHit, 0); distanceSet = false; } } } 

Explique este trecho de código:

  1. Se algumas posições forem armazenadas na lista ropePositions , então ...
  2. Filmamos a partir da posição do jogador na direção do jogador que olha a última posição da corda da lista - o ponto de referência no qual o gancho do gato se conectou à pedra - com uma distância de raio igual à distância entre o jogador e a posição do ponto de referência da corda.
  3. Se o raycast colidir com algo, o colisor deste objeto será convertido com segurança para o tipo PolygonCollider2D . Embora seja um PolygonCollider2D verdadeiro, a posição do vértice mais próximo desse colisor é retornada usando o método que escrevemos anteriormente como Vector2 .
  4. Ele é verificado pelo wrapPointsLookup para garantir que a mesma posição não seja verificada novamente. Se estiver marcado, descartamos a corda e a cortamos, deixando o jogador cair.
  5. Em seguida, a lista ropePositions é ropePositions : a posição é adicionada em torno da qual a corda deve ser enrolada. O dicionário wrapPointsLookup também é atualizado. Finalmente, o sinalizador distanceSet é redefinido para que o método UpdateRopePositions() possa redefinir a distância da corda com o novo comprimento da corda e segmentos.

Em ResetRope() adicione a seguinte linha para que o dicionário wrapPointsLookup limpo toda vez que um jogador desconectar uma corda:

 wrapPointsLookup.Clear(); 

Salve e inicie o jogo. Atire o gancho do gato na pedra acima da lesma e use a ferramenta Mover na janela Cena para mover a lesma sobre várias bordas da pedra.


Foi assim que ensinamos a corda a envolver objetos!

Adicionar capacidade de balanço


A lesma pendurada na corda é bem estática. Para corrigir isso, podemos adicionar a capacidade de oscilação.

Para fazer isso, precisamos obter uma posição perpendicular à posição de balançar para a frente (lateralmente), independentemente do ângulo em que ele está olhando.

Abra PlayerMovement.cs e adicione as duas variáveis ​​públicas a seguir na parte superior do script:

 public Vector2 ropeHook; public float swingForce = 4f; 

A variável ropeHook receberá uma posição em que o gancho da corda está localizado no momento, e swingForce é o valor que usamos para adicionar o movimento de giro.

Substitua o método FixedUpdate() novo:

 void FixedUpdate() { if (horizontalInput < 0f || horizontalInput > 0f) { animator.SetFloat("Speed", Mathf.Abs(horizontalInput)); playerSprite.flipX = horizontalInput < 0f; if (isSwinging) { animator.SetBool("IsSwinging", true); // 1 -          var playerToHookDirection = (ropeHook - (Vector2)transform.position).normalized; // 2 -  ,     Vector2 perpendicularDirection; if (horizontalInput < 0) { perpendicularDirection = new Vector2(-playerToHookDirection.y, playerToHookDirection.x); var leftPerpPos = (Vector2)transform.position - perpendicularDirection * -2f; Debug.DrawLine(transform.position, leftPerpPos, Color.green, 0f); } else { perpendicularDirection = new Vector2(playerToHookDirection.y, -playerToHookDirection.x); var rightPerpPos = (Vector2)transform.position + perpendicularDirection * 2f; Debug.DrawLine(transform.position, rightPerpPos, Color.green, 0f); } var force = perpendicularDirection * swingForce; rBody.AddForce(force, ForceMode2D.Force); } else { animator.SetBool("IsSwinging", false); if (groundCheck) { var groundForce = speed * 2f; rBody.AddForce(new Vector2((horizontalInput * groundForce - rBody.velocity.x) * groundForce, 0)); rBody.velocity = new Vector2(rBody.velocity.x, rBody.velocity.y); } } } else { animator.SetBool("IsSwinging", false); animator.SetFloat("Speed", 0f); } if (!isSwinging) { if (!groundCheck) return; isJumping = jumpInput > 0f; if (isJumping) { rBody.velocity = new Vector2(rBody.velocity.x, jumpSpeed); } } } 

As principais mudanças aqui são que a bandeira é verificada primeiro isSwingingpara que as ações sejam executadas apenas quando a lesma está pendurada na corda, e também adicionamos uma perpendicular ao canto da lesma, indicando seu ponto de ancoragem atual na parte superior da corda, mas perpendicular à direção de seu balanço.

  1. Obtemos o vetor de direção normalizado do player para o ponto de conexão do gancho.
  2. Dependendo se a lesma balança para a esquerda ou para a direita, a direção perpendicular é calculada usando playerToHookDirection. Uma chamada de depuração também foi adicionada para que você possa vê-la no editor, se desejar.

Abra o RopeSystem.cs e adicione o seguinte na parte superior do bloco else, dentro do if(!ropeAttached)método Update():

 playerMovement.isSwinging = true; playerMovement.ropeHook = ropePositions.Last(); 

No bloco if do mesmo design, if(!ropeAttached)adicione o seguinte:

 playerMovement.isSwinging = false; 

Assim, informamos ao script PlayerMovement que o jogador está oscilando e também determinamos a última (exceto a posição do jogador) da corda - em outras palavras, o ponto de ancoragem da corda. Isso é necessário para calcular o ângulo perpendicular que acabamos de adicionar ao script PlayerMovement.

Aqui está como é que você liga os aparelhos em um jogo de corrida e pressiona A ou D para girar para a esquerda / direita:


Adicionando descida de corda


Embora não tenhamos a capacidade de subir e descer a corda. Embora na vida real a lesma não possa subir e cair facilmente ao longo da corda, mas este é um jogo no qual tudo pode acontecer, certo?

Na parte superior do script RopeSystem, adicione duas novas variáveis ​​de campo:

 public float climbSpeed = 3f; private bool isColliding; 

climbSpeeddefinirá a velocidade na qual a lesma pode subir e descer a corda e isCollidingserá usado como uma bandeira para determinar se a propriedade da junta de distância da corda da junta de distância pode ser aumentada ou diminuída.

Adicione este novo método:

 private void HandleRopeLength() { // 1 if (Input.GetAxis("Vertical") >= 1f && ropeAttached && !isColliding) { ropeJoint.distance -= Time.deltaTime * climbSpeed; } else if (Input.GetAxis("Vertical") < 0f && ropeAttached) { ropeJoint.distance += Time.deltaTime * climbSpeed; } } 

Este bloco if..elseiflê a entrada ao longo do eixo vertical (para cima / baixo ou W / S no teclado) e, levando em consideração as bandeiras, ropeAttached iscCollidingaumenta ou diminui a distância ropeJoint, criando o efeito de alongar ou encurtar o cabo.

Conectamos esse método, adicionando sua chamada ao final Update():

 HandleRopeLength(); 

Também precisamos de uma maneira de definir a bandeira isColliding.

Adicione os dois métodos a seguir na parte inferior do script:

 void OnTriggerStay2D(Collider2D colliderStay) { isColliding = true; } private void OnTriggerExit2D(Collider2D colliderOnExit) { isColliding = false; } 

Esses dois métodos são nativos da classe base dos scripts MonoBehaviour.

Se atualmente o Collider tocar em outro objeto físico no jogo, o método será acionado constantemente OnTriggerStay2D, atribuindo um isCollidingvalor à bandeira true. Isso significa que quando a lesma toca a pedra, é atribuído um valor à bandeira isColliding true.

O método é OnTriggerExit2Dacionado quando um colisor sai da área de outro colisor, configurando o sinalizador como falso.

Lembre-se: o método OnTriggerStay2Dpode ser muito computacionalmente caro, portanto, use-o com cuidado.

Para onde ir a seguir?


Inicie o jogo novamente e desta vez pressione as teclas de seta ou W / S para mover para cima e para baixo na corda.


O projeto final desta parte do tutorial pode ser baixado aqui .

Percorremos um longo caminho - desde a lesma que não balança até o molusco gastrópode acrobático sem casca!

Você aprendeu a criar um sistema de mira capaz de disparar um gancho de gato em qualquer objeto que tenha um colisor, agarrar-se a ele e balançar simultaneamente nele, girando em uma corda dinâmica em torno das bordas dos objetos! Bom trabalho.


No entanto, uma função importante está faltando aqui - a corda não pode "desenrolar" quando necessário.

Na segunda parte do tutorial, resolveremos esse problema.

Mas se você estiver disposto a arriscar, por que não tentar fazer isso sozinho? Você pode usar um dicionário para isso wrapPointsLookup.

Source: https://habr.com/ru/post/pt414887/


All Articles