sábado, 8 de junho de 2013

Controlando um jogo feito em Unity com com arduino e um acelerometro.

(Se deseja ir direto para implementação pule para o passo 1.)
A antiga(mas não ultrapassada) forma de controlar jogos através de um direcional e botões, limita a construção, ou mesmo as interações possíveis com os jogos. Visando essa sentença, cada vez mais os jogos e o entretenimento moderno está em busca por um diferencial, seja por uma característica única no software ou por uma forma diferente de construir essa interação.

                A proposta a seguir visa uma diferente abordagem na construção de um controle com giroscópio e acelerômetro, mais comum em celulares ou portáteis por já conter esse tipo de ferramenta.



1 – Materiais Utilizados
4 fios.

1 Arduino Uno.

1 Giroscopio/Acelerometro 
Modelo utilizado: GY-521 (MPU6050)

e BreadBoard.

2 – Montagem dos materiais.
Ligamos o Giroscopio no arduino através das portas SCL e SDA. O VCC na porta de alimentação de 5V ou 3.3V e o GND no ground.





3 – Código utilizado no arduino.

Utilizando a FreeIMU library : 
1:  #include <ADXL345.h>  
2:  #include <bma180.h>  
3:  #include <HMC58X3.h>  
4:  #include <ITG3200.h>  
5:  #include <MS561101BA.h>  
6:  #include <I2Cdev.h>  
7:  #include <MPU60X0.h>   
8:  #include <EEPROM.h>  
9:  //#define DEBUG  
10:  #include "DebugUtils.h"  
11:  #include "FreeIMU.h"  
12:  #include <Wire.h>  
13:  #include <SPI.h>  
14:  float val[11]; //Vetor que irão conter as variaveis  
15:  FreeIMU my3IMU = FreeIMU();  
16:  int y = 0;  
17:  int z = 0;  
18:  int media[3];  
19:  void sicronizar()  
20:  {  
21:   my3IMU.getValues(val);  
22:   for(int i = 0; i <50; i++)  
23:   {  
24:    media[0] += map(val[0], -17090, 17090, -45, 45);    
25:    media[1] += map(val[1], -17090, 17090, -45, 45);    
26:    media[2] += map(val[2], -17090, 17090, -45, 45);   
27:   }  
28:   media[0] /= 50;  
29:   media[1] /= 50;  
30:   media[2] /= 50;  
31:  }  
32:  void setup() {   
33:   Serial.begin(115200);  
34:   Wire.begin();  
35:   delay(500);  
36:   my3IMU.init(true); // Variavel que ativa o modo rápido  
37:   sicronizar();  
38:   delay(500);  
39:  }  
40:  void loop(){  
41:   Serial.flush(); //Limpa o buffer.   
42:   my3IMU.getValues(val); //Passa o vetor como parametro  
43:   val[1] = map(val[1], -17090, 17090, -45, 45);  /* Mapeando as variaveis */  
44:   val[2] = map(val[2], -17090, 17090, -45, 45);  /* para ir de -45 a +45  */  
45:   z = val[1] - media[1];  
46:   y = val[2] - media[2];  
47:   Serial.print(y);  
48:   Serial.print(',');  
49:   Serial.println(z);  
50:   delay(100);  
51:  }  
A função do algoritmo é: ler o acelerômetro, obter os valores necessários  e imprimi-los na saída serial. 

Pontos Relevantes:

A função “my3IMU.getValues(val);” atriubui a um vetor as as variaveis associadas ao acelerometro, giroscopio, sensor de movimento e temperatura. No caso so trabalharemos com as posições 1 e 2 (Eixo de rotação Z e Y respectivamente, pode variar com o acelerometro).

A função sicronizar() faz 50 loops pegando as variaveis de Y e Z e faz uma media nelas, para calcular a posição média inicial do acelerometro.


“Serial.print(y)”, “Serial.print(',')” e “Serial.println(z)” e imprimi em uma linha de saida serial o conteudo das variaveis y e z separados por virgula e pula a linha.

4 – Código em C# utilizado no jogo.
O jogo foi feito utilizando Unity pela facilidade de construção, e entendimento mais direcionado a comunicação e a obtenção de dados do acelerômetro. O código a seguir é utilizado no tabuleiro do jogo.
1:  using UnityEngine;  
2:  using System.Collections;  
3:  using System.IO.Ports;  
4:  public class SerialTeste : MonoBehaviour {  
5:       public float speed;  
6:       private float amountToMove;  
7:       SerialPort sp = new SerialPort("COM5", 115200);  
8:       int d = 0;  
9:       int dOld = 0;  
10:       int dY = 0;  
11:       int dYOld = 0;  
12:       int lastX = 0;  
13:       int lastY = 0;  
14:       // Use this for initialization  
15:       void Start () {  
16:            sp.Open();  
17:            sp.ReadTimeout = 2;  
18:       }  
19:       // Update is called once per frame  
20:       void Update () {  
21:            amountToMove = speed * Time.deltaTime;  
22:            if(sp.IsOpen) {  
23:                 try {  
24:                      sp.DiscardOutBuffer();  
25:                      string value = sp.ReadLine();  
26:                      string[] vec2 = value.Split(',');  
27:                      if(vec2[0] != "" && vec2[1] != ""){  
28:                           lastX = int.Parse(vec2[0]);  
29:                            lastY = int.Parse(vec2[1]);  
30:                           MoveObject(int.Parse(vec2[0]),int.Parse(vec2[1]));  
31:                           //print ("X:"+int.Parse(vec2[0])+"Y:"+int.Parse(vec2[1]));  
32:                      }  
33:                      sp.BaseStream.Flush();  
34:                 } catch (System.Exception e) {  
35:                  MoveObject(lastX,lastY);  
36:                 }  
37:            }  
38:       }  
39:       void MoveObject(int DirectionX, int DirectionY) {  
40:            this.d = DirectionX;  
41:            this.dY = DirectionY;  
42:            print(this.d);  
43:            int rotateZ = -(this.dY - dYOld);  
44:            int rotateX = this.d - dOld ;  
45:            if((System.Math.Abs(rotateX) > 2 && System.Math.Abs(rotateX) < 45) || (System.Math.Abs(rotateZ) > 2 && System.Math.Abs(rotateZ) < 45))  
46:            {  
47:            var newRotation = Quaternion.LookRotation(Camera.main.transform.position - transform.position).eulerAngles;  
48:                 /*newRotation.y = 0;  
49:                 newRotation.x = rotateX;  
50:                 newRotation.z = rotateZ;  
51:                 transform.rotation = Quaternion.Euler(newRotation);*/   
52:                 transform.Rotate(      //Rotacionar a camera baseada na nova posição comentar se for usar quarternions.  
53:                  rotateX ,  
54:                  0,  
55:                  rotateZ  
56:                );  
57:            dOld = d;  
58:            dYOld = dY;  
59:            }  
60:            Vector3 ang = transform.localEulerAngles;  
61:            if (this.d == 0)  
62:            {  
63:                 transform.Rotate(-ang.x,0,0);  
64:            }  
65:            if (this.dY == 0 )  
66:            {  
67:                 transform.Rotate(0,0,-ang.z);  
68:            }  
69:            if (ang.y != 0 )  
70:            {  
71:                 transform.Rotate(0,-ang.y,0);  
72:            }  
73:       }  
74:  }  
Pontos Relevantes: Linha 22 à 35: Recebe os dados da porta serial, trata e usa eles para rotacionar o objeto Linha 52: transform.Rotate Rotaciona o objeto com as coordenadas atualizadas. Linha 57 à 72: Estabiliza o tabuleiro quando os angulos forem 0.

Um comentário:

  1. Olá Marcelo, beleza?
    Parabens pelo post, gostaria de pedir seu contato para tirar umas duvidas referente ao projeto, se puder, me adciona no face https://www.facebook.com/julio.chiles ou manda um email: julio_g.angel@hotmail.com.
    Ficarei muito agradecido.
    E novamente, parabens pelo post

    ResponderExcluir