P5.B Arrays e funções

 

Para transformar o piano numa fechadura secreta que abre a caixa apenas quando uma melodia é tocada necessitamos de implementar variáveis especiais do tipo Array.

Uma array é uma variável que, ao contrários das restantes variáveis, contém vários compartimentos numerados que podem ser acedidos por um index ou número.

Se considerarmos que as variáveis convencionais são como uma gaveta, que tem um label e guardam um valor no seu interior, nessa metáfora as variáveis do tipo array seriam uma estante, composta por várias gavetas, cada uma delas numerada. Outra forma de visualizar uma array é como se tratando uma lista de valores.

Vamos criar uma lista que vai conter a sequência secreta de notas musicais que forma a melodia que abrirá a caixa. Essa lista é pública e será associada ao gameobject teclado.

 

1.Arrays

1.1 Crie um novo script, com o nome “fechadura”, e associe-o ao teclado.

 

1.2 Declare uma nova variável array chamada “melodia” do tipo string. Vamos declarar esta variável como sendo pública para permitir que se possa introduzir a sequências de notas directamente a partir da interface do Unity. Outra vantagem deste método é que deste modo podemos reutilizar varias vezes o teclado em outros pontos do jogo e associar cada um deles uma melodia secreta distinta.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class fechadura : MonoBehaviour {

        public string []melodia;

        void Start () {
        }
        void Update () {
        }

}

 

Como a array é pública na interface do Unity3D surge uma inputbox para definir a dimensão da lista, ou seja, o número de notas da nossa melodia secreta. Por uma questão de simplicidade, vamos definir a melodia secreta com as primeiras 4 notas da música popular Frei João: dó, ré, mi, dó.

image001

 

1.3 Necessitamos, igualmente, de uma segunda array, com o nome “notas”, que terá como finalidade guardar as últimas notas que o jogador tocou. A sua dimensão será igual à da melodia secreta, por essa razão, vamos recorrer à propriedade length para obter conhecer a dimensão da array melodia.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class fechadura : MonoBehaviour {
        public string[] melodia;
        public string [] notas;

        void Start () {
               notas=new string [ melodia.Length ];
        }
        void Update () {
        }
}


 

2. Funções

O próximo passo é criar uma função que aceita como argumento a nota tocada pelo jogador e verifica se as notas tocadas correspondem às da melodia secreta. Caso sejam idênticas a função devolve verdadeiro.
A estratégia consistem em tornar esta função pública, de modo a ficar acessível do script que corre em cada uma das teclas individuais.

 

2.1 Comecemos por declarar a função com o nome “tocouMelodiaSecreta” logo a seguir ao evento update.

public bool tocouMelodiaSecreta (string nota) {
 
}

O termo bool significa que a função vai retornar um valor do tipo booleano (verdadeiro ou falso). No interior do parêntesis, declaramos a variável local e temporária relativa à última nota.

 

2.2 A primeira tarefa da função “tocouMelodiaSecreta” é guardar a última nota na array “notas”.

Por exemplo, se a array tem 4 elementos, é necessário “empurrar” os valores ao longo da lista. Suponhamos que antes da função ser chamada a array tem as seguintes 4 notas:

notas[0]=”fá”
notas[1]=”sol”
notas[2]=”lá”
notas[3]=”si”

O jogador tocou na tecla “ré” e é chamada a função “tocouMelodiaSecreta”.

Depois de executada a array deveria ficar actualizada com última nota tocada e apresentar os seguinte valores:

notas[0]=”sol”
notas[1]=”lá”
notas[2]=”si”
notas[3]=”ré”

Repare que estas são agora as úlitmas 4 notas tocadas pelo jogador.

Na posição nº0 foi gravado o valor da antiga posição nº1.
Na posição nº1 foi gravado o valor da antiga posição nº2.
Na posição nº2 foi gravado o valor da antiga posição nº3.
Na posição nº3 foi gravado o última nota tocada pelo jogador

Tendo em consideração que a dimensão da melodia secreta é variável e não é fixa, não podemos implementar a estratégia de gravação acima descrita dado que só funcionaria para melodias com 4 notas.

O mecanismo tem de ser dinâmico e flexível para servir todos os outros casos.

A solução passa por recorrer a um ciclo que vai iterar todos os elementos da lista e, para cada um, gravar o valor da posição anterior.

 

public bool tocouMelodiaSecreta(string nota){
  for (int i = 1; i < notas.Length; i++) {
    notas [i-1] = notas [i];
  }
  notas [notas.Length-1] = nota;
  return true;
}

 

O ciclo for cria uma variável i que vai variar entre o valor 1 e a antepenúltima posição da array. A preposição no interior do ciclo grava para cada posição, o valor da posição seguinte. Quando termina o ciclo, é introduzida na úlitma posição a nota passada pelo argumento da função, que corresponde à última nota. A instrução return true está presente para o Unity não dar erro, uma vez que ele espera que a função retorne  uma valor do tipo boolean.

2.3 Em seguida vamos testar a função. No script “tecla”, associado a cada uma das teclas, vamos chamar a função com o valor da nota pressionada pelo jogador.

Reescreva o evento OnMouseDown de modo a ficar idêntico ao seguinte:

void OnMouseDown(){
   print (" tocou no " + this.nomeTecla);
   this.GetComponent<Animator> ().Play ("carrega");
   this.GetComponent<AudioSource> ().Play ();
   this.GetComponentInParent<fechadura> ().tocouMelodiaSecreta (this.nomeTecla);
}

Adicionámos um comando que procura no gameobject pai (que será o teclado, uma vez que este script corre numa das teclas e estas são seus filhos) o script “fechadura” e chama a função “tocouMelodiaSecreta”. Se esta não fosse declara pública não iria aparecer após digitarmos o ponto. Repare que chamamos a função passando no argumento o valor da última tecla.

Podemos ver a função a funcionar reproduzindo a cena e, não esquecendo de ter o teclado selecionado para que os valores possam ser monitorizados no inspector.

image002

À medida  que as teclas vão sendo tocadas a array notas vai sendo actualizada apresentando as últimas 4 notas tocadas.
2.4 Agora que o mecanismo de actualização e registo das últimas teclas tocadas pelo jogador se encontra implementado, estamos preparados para comparar esta última array com a array que contém a melodia secreta.

A estratégia consistem e criar uma variável  booleana com o nome “melodiaCorreta” e atribuir-lhe um valor inicial de true.

Em seguinte, um ciclo vai percorrer todos os elementos das array e compará-los. Caso um dos elementos não coincida (basta um) mudaremos o seu valor para false. Assim, quando terminar o ciclo, a variável “melodiaCorreta” é true ou false consoante as notas das duas arrays sejam idênticas ou não. Finalmente, usaremos o valor dessa variável para retornar o valor da função.

public bool tocouMelodiaSecreta(string nota){

  // actualizar a úlitma nota
  for (int i = 1; i < notas.Length; i++) {
    notas [i-1] = notas [i];
  }
  notas [notas.Length-1] = nota;

  // verificar se arrays são idênticas
  bool melodiaCorreta = true;
  for (int i = 0; i < notas.Length; i++) {
    if (notas [i] != melodia [i]) melodiaCorreta =false;
  }

  // termminar a função e retornar o valor
  return melodiaCorreta;
}

 

E no script da tecla vamos também introduzir a condição que chama a função e abre a caixa em função do valor retornado.

 

void OnMouseDown(){
   print (" tocou no " + this.nomeTecla);
   this.GetComponent<Animator> ().Play ("carrega");
   this.GetComponent<AudioSource> ().Play ();
   this.GetComponentInParent<fechadura> ().tocouMelodiaSecreta (this.nomeTecla);



  if (this.GetComponentInParent<fechadura> ().tocouMelodiaSecreta (this.nomeTecla)){
    GameObject.Find ("frente").GetComponent<Animator> ().Play ("abre_segredo");
  }
}

 

Teste o cenário. Toque a sequência de notas correcta e verifique se a caixa abre.