Skip navigation

hoje analisamos colecções dinâmicas de dados, classes em arraylists. derivação de subclasses, uso de várias classes dentro de uma arraylist; detecção e resposta de colisão entre objectos da mesma classe e de outras classes.  

 

 

1. arraylists


são colecções dinâmicas de dados, uma array que se inicia sem elementos, onde podemos adicionar elementos, aceder ao número e aos dados dos elementos na arraylist, e podemos ainda remover dinamicamente qualquer elemento. são sistemas dinâmicos muito parecidos ao uso das arrays. declaramos as variáveis:

/// arraylist como colecção dinâmica de elementos

ArrayList parts;

PFont f;

float friccao = 0.961;

construímos a arraylist assim:

  parts = new ArrayList(); //criar uma arraylist vazia  

e inserimos elementos para lá ao criarmos um elemento local, e posteriormente chamarmos a função add das arraylists com o elemento criado:

  Part novaPart = new Part(width/2,height/2);  

  parts.add(novaPart); // adicionar um elemento  

na função draw inserimos mais partículas se mousePressed:

  if(mousePressed){    

Part p = new Part(mouseX,mouseY);    

parts.add(p);  

}

e lemos o número actual de elementos: int num_elementos = parts.size();  cada elemento é do tipo de dados que inserimos dentro da arraylist, neste caso, Part. para renderizarmos todos o sistema lemos todos os objectos que fazem parte da arraylist, e ou o removemos da arraylist se ele já não estiver activo, ou o renderizamos, que faz funções de update de posições e emite o comando gráfico que desenha o elemento. tal como nas arrays, vamos usar um ciclo for para aceder a cada elemento:

  for (int i=0; i<parts.size();i++){    

Part p = (Part) parts.get(i);    

if(!p.active)       parts.remove(i);    

else       p.render();  

}

enquanto que nas arrays acedemos aos elementos com o índice dentro de parentisis rectos, nas arraylists lemos os elementos através da função get(i):

parts[i].active, parts[i].render() // aceder a elementos e funções de obj em arrays

Part p = (Part )parts.get(i); // aceder ao elemento i da arraylist

p.active, p.render() // aceder a elementos e funções de obj em arraylists  

estas são as funções principais das arraylists que mais vamos utilizar: inserir elementos para a arraylist, aceder a campos e métodos dele, e removê-lo da arraylist. parts.add(Part elemento), parts.get(int numero_do_elemento), parts.remove((int numero_do_elemento), int numero_de_elementos = parts.size();  

arraylist-00083

arraylist-00264

 

 

2. extensão de classes


todas as classes podem ser extendidas. ao extendermos, criamos uma nova classe que tem todos os campos e funções da classe base, e criamos um novo tipo de dados que extende o tipo de dados anterior. assim, temos a nossa classe Part:  

class Part{

  float x,y,vx,vy, energy;

  boolean active=true;

 

  Part(float _x, float _y){

    x = _x;

    y = _y;

    vx = random(-5,5);

    vy = random(-5,5);

    energy = 255.0f;    

  } 

 

  void render(){

    update();

    fill(255,energy);

    ellipse(x,y,20,20); 

  }

 

  void update(){

    x += vx;

    y += vy;

    vx *= friccao;

    vy *= friccao; 

    energy -= 1.5;

    if (energy<0.)

      active=false;    

  }

}

 

esta classe tem x,y,vx,vy,… como campos, e render(), update() como funções. definimos agora uma classe Part2 que extende e modifica o comportamento de funções:  

class Part2 extends Part{

 

  Part2(float _x, float _y){    

    super(_x,_y); // chamar o construtor de Part com parâmetros

    energy_dec = random(0.5,1.2); // re-escrever o valor de energy_dec, para ficarem mais tempo activas

  }

 

  void render(){ // um render de Part2, diferente de Part

    update(); // chamar a update de Part, não está aqui presente

    fill(255,energy);

    float amt = energy*0.005;

    ellipse(x+random(-amt,amt) , y+random(-amt,amt) , 50,50); // raio maior, posições random

  }

}

a classe Part2, além dos métodos de Part, tem agora estes novos construtor e função render. a função render substitui a render de Part, e desenha as coisas de maneira diferente, mas chama na mesma a função update que apenas está definida na class Part, e não aqui. como esta classe extende a Part, acedemos aos métodos da classe como se eles já estivessem declarados.   estas duas classes são agora inseridas na mesma arraylist, uma array que fica quer com elementos Part, quer com elementos Part2. como os elementos de Part2 são extendidos de Part, podemos tratá-los como se fossem Part na leitura dos elementos.  

  //adicionar 2 elementos de classes diferentes para 1 arraylist

  parts = new ArrayList(); //criar uma arraylist vazia

  Part novaPart = new Part(width/2,height/2);

  Part2 novaPart2 = new Part2(width/2,height/2);

  parts.add(novaPart); // adicionar um elemento

  parts.add(novaPart2); // adicionar um elemento

 

  // aceder aos elementos Part e Part2

  for (int i=0; i<parts.size();i++){

    Part p = (Part) parts.get(i);

    p.render();

    (if !p.active)

parts.remove(i);

arraylist-000821

 

fizemos a mesma coisa com uma classe Part3, que extende Part:  

class Part3 extends Part{

 

  Part3(float _x, float _y){    

    super(_x,_y);

    energy_dec = random(0.5,1.2);

  }

 

  void render(){

    update();

    fill(random(200),random(200),random(200),energy);  // cores aleatórias

    float amt = energy*0.005;

    ellipse(x+random(-amt,amt) , y+random(-amt,amt) , 50,50); 

  }

}

arraylist-00072

arraylist-00277

 

 

 

3. detecção e resposta de colisões entre objectos  

 

primeiro vimos os exemplos bounce e depois o bouncybubbles dos exemplos do processing (file>examples>topics>motion). no bounce vimos como é que se colide com a periferia da tela. se a posição x é menor que (0+raio), então está a colidir com a parede esquerda logo deve inverter a velocidade do eixo do x. e se a posição x é superior a (largura+raio), a mesma situação. isto está definido nos testes if. if xpos maior que largura-size ou xpos<0, então inverter direcção x, que multiplica por um valor de velocidade. o mesmo acontece no eixo dos y’s.  

  // Update the position of the shape

  xpos = xpos + ( xspeed * xdirection );

  ypos = ypos + ( yspeed * ydirection );

 

  // Test to see if the shape exceeds the boundaries of the screen

  // If it does, reverse its direction by multiplying by -1

  if (xpos > width-size || xpos < 0) {

    xdirection *= -1;

  }

  if (ypos > height-size || ypos < 0) {

    ydirection *= -1;

  }

bounce-00021

bounce-00067

 

depois analisamos o bouncybubbles, aí já há um óptimo método de detecção de colisão e resposta no caso colisão circulo-circulo. são colisões com um factor de elasticidade que pode ser ajustado para colisões ríspidas, ou colisões elásticas. a lógica é muito semelhante ao que já temos analisado. além do update e render, vamos ter um método que vai efectuar a colisão da bola actual com todas as restantes bolas. este método colide é executado a cada frame do programa, e nele testam-se as posições da bola actual contra todas as posições das restantes bolas que fazem parte da array Balls. neste sketch executamos as funções colide, move e display a cada bola.   

 

void draw() 

{

  background(0);

  for (int i = 0; i < numBalls; i++) {

    balls[i].collide();

    balls[i].move();

    balls[i].display();  

  }

}

a colide vai afectar as velocidades das partículas se a soma dos raios de cada elemento for inferior à distância entre os centros, a situação de colisão. nesse caso, calcula-se o ângulo da colisão, a posição xy óptima dessa partícula como resposta a colisão, andar na direcção do vector de colisão a distância da soma dos raios das partículas; esta posição xy target vai ser suavizada com um factor de spring, o tal factor de elasticidade que controla a suavidade da resposta à colisão. o resultado encontrado são acelerações que devemos acumular nas velocidades actuais das partículas. a nossa partícula (velocidades vx e vy) são diminuídas as acelerações, às velocidades do outro círculo, são adicionadas as acelerações. a seguir a move encarrega-se de adicionar as novas velocidades às posições actuais das bolas.  

 

  void collide() {

    for (int i = id + 1; i < numBalls; i++) {

      float dx = others[i].x – x;

      float dy = others[i].y – y;

      float distance = sqrt(dx*dx + dy*dy);

      float minDist = others[i].diameter/2 + diameter/2;

      if (distance < minDist) { 

        float angle = atan2(dy, dx);

        float targetX = x + cos(angle) * minDist;

        float targetY = y + sin(angle) * minDist;

        float ax = (targetX – others[i].x) * spring;

        float ay = (targetY – others[i].y) * spring;

        vx -= ax;

        vy -= ay;

        others[i].vx += ax;

        others[i].vy += ay;

      }

    }   

  }

bouncybubbles-000801

bouncybubbles-001281

de seguida, criamos uma modificação neste sketch, uma versão com arraylists, onde podemos adicionar as partículas que quisermos ao carregar no rato. 

  // iniciar a arraylist:

  for (int i = 0; i < numBalls; i++) {

    Ball b = new Ball(random(width), random(height), random(20, 40), i, balls);

    balls.add(b);

  }

e a função draw, onde além de gerirmos as partículas, adicionamos novos elementos na posição xy do rato:

void draw() {

  background(0);

  for (int i = 0; i <  balls.size(); i++) {

    Ball b = (Ball) balls.get(i);

    b.collide();

    b.move();

    b.display();  

  }

  if(mousePressed){

    if(frameCount%10==0){

       Ball b = new Ball(mouseX,mouseY, random(20, 40), balls.size(), balls); 

       balls.add(b);

    }

  }  

}

 

  bounce_array-008522

bounce_array-01060

 

 

 

4. colisões entre objectos de classes diferentes  

 

um motor mais elaborado terá diferentes classes de objectos que têm comportamentos distintos entre si, se actualizamos posições ou se estão estáticos, que dimensões ocupam, como os desenhamos de maneira diferente, etc. para isto, devemos criar várias classes de objectos, onde cada classe define o comportamento específico de cada objecto. mantemos uma array ou várias arraylists de objectos e dentro dos métodos de colide de cada elemento que pode colidir, temos de testar todas as posições actuais dos elementos que poderão colidir.   para simplificar fomos criar uma classe de bolas, e outra de obstáculos circulares. os obstáculos não se movem, são apenas posições, raio. as bolas é que têm posições, velocidades, raio, e como se deslocam, terão também uma função colide que implementará a colisão elástica que vimos anteriormente.  

class Obstaculo{

  float x,y,r;

  Obstaculo(){

    x = random(width);

    y = random(height);

    r = random(20,50);

  }

  void render(){

   draw(); 

  }

  void draw(){

    fill(100,50,0,250);

    ellipse (x,y,r*2,r*2);

  }

}

 

class Bola{

  float x,y,r, vx,vy;

  Bola(){

    x = random(width);

    y = random(height);

    r = random(10,20);

    vx = random(-5,5);

    vy = random(-5,5);

  }

  void render(){

    colide();

   update();

   draw(); 

  }

  void colide(){

  }

  void update(){

    x+=vx;

    y+=vy;

    //bounds

    if(x<0)  {x=0; vx = -vx;}

    if(x>width)  {x=width; vx = -vx;}

    if(y<0)  {y=0; vy = -vy;}

    if(y>height)  {y=height; vy = -vy;}

  }

  void draw(){

    fill(100,200);

    ellipse (x,y,r*2,r*2);

  }

tendo estas duas classes, fazemos duas arraylists globais onde vamos armazenar os vários elementos:  

ArrayList bolas = new ArrayList();

ArrayList obstaculos = new ArrayList();

 

a draw chama a render de cada classe:  

 

void draw(){

 background(0);

 for(int i=0; i<obstaculos.size(); i++){

  Obstaculo o = (Obstaculo) obstaculos.get(i);

  o.render();

 } 

 for(int i=0; i<bolas.size(); i++){

  Bola b = (Bola) bolas.get(i);

  b.render();

 } 

}

na render dos obstáculos, apenas gráficos, na render das bolas, chamamos a colide primeiro, depois a update e finalmente a draw. inicialmente não escrevemos nada na colide para apenas ver os elementos todos sem qualquer tipo de comunicação entre eles.

bolasobstaculos-00099

 

num segundo sketch bolas_e_obstaculos_arraylist fomos implementar colisão entre as bolas e os obstáculos. para tal, na função colide da classe Ball foi adicionado um loop for que detecta a colisão entre as coordenadas actuais da bola e de todos os obstáculos. se a distância dos centros for inferior à soma dos raios sabemos que colidem, logo afectamos apenas a velocidade da bola, que é posteriormente actualizada na update.

 

bolasobstaculos2-01166

 

aqui as bolas colidem com os obstáculos, mas não entre elas. para que isso aconteça, vamos num terceiro sketch introduzir colisão entre cada uma das bolas com as restantes da arraylist. aí a função colide, além de colidir com os obstáculos, vai ter um loop for que percorre todos os elementos das bolas, como já analisámos no ponto 3. deste post. acedemos aos elementos da arraylist de obstáculos, e acedemos aos restantes elementos da arraylist bolas. o comportamento do sistema físico agora é bem mais interessante.

 

bolasobstaculos3-02785

bolasobstaculos3-01604

  no quarto sketch, introduzimos uma bola que se controla com o rato, e com a qual podemos interagir com as testantes bolas do sistema.  

ArrayList bolas = new ArrayList();

ArrayList obstaculos = new ArrayList();

Bola ze;

void draw(){

 background(0);

 ze.x = mouseX;

 ze.y = mouseY;

 ze.vx = mouseX-pmouseX;

 ze.vy = mouseY-pmouseY;

 

 for(int i=0; i<obstaculos.size(); i++){

  Obstaculo o = (Obstaculo) obstaculos.get(i);

  o.render();

 } 

 

 for(int i=0; i<bolas.size(); i++){

  Bola b = (Bola) bolas.get(i);

  b.render(i);

  ze.colide(i);

 } 

 

  ze.draw();

 

}

 no quinto sketch adicionamos uma nova classe Destino, uma bola verde que é comparado na colisão das bolas, mais um elemento a colidir com, além dos obstáculos e das outras bolas, se a distância for menor, sabemos que colidem, e são removidos do sistema, e adicionados a um score que vai incrementando.

 

bolasobstaculos5-01263

  o sexto skecth introduz som, quando os elementos colidem com a bola verde ocorre um som de um banco de sons. ainda vimos alguns exemplos de sistemas de colisão física mais avançados, como é o caso do jbox2d e do jbullet. vejam os applets exemplo que eles têm. bom resto de semana.

Anúncios

One Comment

  1. Its like you read my mind! You seem to know a lot about
    this, like you wrote the book in it or something. I think that you could do with some pics
    to drive the message home a bit, but other than
    that, this is great blog. A fantastic read. I will definitely
    be back.


2 Trackbacks/Pingbacks

  1. By sessão 10 « O Som do Pensamento on 26 Mar 2009 at 3:44 pm

    […] do Pensamento Curso de programação de ambientes jogáveis About / Infoprojectos « sessão 9 exemplos sessão 10 […]

  2. By sessão 17 « O Som do Pensamento on 27 Maio 2009 at 11:55 am

    […] arraylists como recipientes dinâmicos de dados, texto no processing, como ler linhas de texto, separar palavras e espalhá-las espacialmente para desenhar. o sistema esférico (semelhante ao polar, mas em 3d), como desenhar pontos no espaço em forma de esfera e modificá-los com base no som. (sessão 9) […]

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

%d bloggers like this: