Partículas Físicas
Neste workshop, você vai usar a p5.js, juntamente com 🌟 FÍSICA 🌟, para animar as partículas coloridas. Ao longo do caminho, você aprenderá:
- como usar a p5.js
- alguns conceitos básicos de física
- quão divertida a programação criativa é
Todas as diferentes partículas atuarão como planetas em órbita. Todas elas aplicarão forças (gravidade) umas às outras, dependendo de sua massa e de sua distância umas das outras. A principal diferença aqui é que elas estão muito mais próximas umas das outras do que os planetas reais. Isto ocorre para não ser necessário esperar um ano inteiro para que uma partícula orbite a outra.
Quando terminar este workshop, você deve ter algo semelhante a isto!
Esse workshop deve levar cerca de 15-20 minutos para completar.
Preparando a instalação
Primeiro você precisa montar seu projeto. Criei um boilerplate básico da p5.js que você pode usar para começar a programar facilmente.
- Vá para https://repl.it/@hcbjcentro/boilerplate-p5js
- E clique em “fork” para fazer uma cópia para que você possa editar
Fazendo uma partícula
O primeiro passo neste projeto é criar uma classe para gerenciar as partículas. Antes de fazer isso, você deve criar um novo arquivo para armazenar a classe para facilitar a legibilidade.
-> 1. Crie um novo arquivo chamado particula.js
.
-> 2. Adicione este arquivo entre as tags <body>
/body>
do arquivo index.html
:
...
<body>
<script src="particula.js"></script>
<script src="desenho.js"></script>
</body>
...
-> 3. Adicione a definição da classe no particula.js
class Particula {}
A classe é apenas uma forma de conectar um conjunto de variáveis e funções em um pacote. Agora que você tem uma classe de partícula, você precisa definir o que a partícula pode fazer.
Dentro de sua recém-criada classe Particula
, adicione:
class Particula {
constructor (x, y, massa) {
// Configura partícula
}
desenha() {
// Desenha partícula
}
aplicaForca(forca) {
// Aplica força à partícula
}
fisica(particula) {
// Usa partícula
}
atualiza() {
// Atualiza partícula
}
}
Acabamos de acrescentar uma lista de funções que os objetos desta classe podem executar. Com a estrutura básica montada, vamos passar por cada função individualmente e adicionar o código.
...
constructor(x, y, massa) {
this.posicao = createVector(x, y)
this.aceleracao = createVector(0, 0)
this.velocidade = createVector(0, 0)
this.massa = massa
// raio = (massa / π) ** 0.5
this.raio = Math.sqrt(this.massa / PI) * ESCALA
// define uma cor aleatória para a partícula
this.cor = color(random(0, 255), random(0, 255), random(0, 255))
}
...
O construtor é usado para criar uma instância de uma classe. Neste caso, nós o usamos para configurar todas as variáveis quando fazemos uma nova partícula. A função createVector()
é fornecida pela p5 para fazer facilmente um objeto vetorial. O que é apenas uma linha 2d ou posição com algumas funções para ajudar a modificar facilmente os valores.
A seguir, vamos adicionar o código para desenhar a partícula.
...
desenha() {
// Retira contorno
noStroke()
// Define o preenchimento para a cor da partícula
fill(this.cor)
// Desenha a partícula
ellipse(this.posicao.x, this.posicao.y, this.raio * 2)
}
...
Esta função é muito mais simples. Ela define a cor utilizando a função fill()
e depois desenha a partícula utilizando a função ellipse()
da p5.
A seguir, vamos adicionar o código para aplicar força à partícula.
...
aplicaForca(forca) {
// aceleração = força / massa
this.aceleracao.add(p5.Vector.div(forca, this.massa))
}
...
Aqui vemos a primeira fórmula da física. Ela calcula a aceleração com que o objeto deve ter com base na força aplicada. Utilizamos a segunda lei do movimento de Newton -> F=ma
ou Força = massa * aceleração
para calcular esta aceleração.
A seguir, vamos adicionar o código para aplicar a física do mundo real a uma partícula.
...
fisica(particula) {
// Não aplicar para a própria partícula
if (this === particula) return
// massa1 * massa2
let massa = this.massa * particula.massa
// raio1 + raio2
let raio = this.raio + particula.raio
// Distância entre as partículas
let distancia = this.posicao.dist(particula.posicao)
// Não aplicar se as partículas estiverem se tocando
if (distancia <= raio) return
// força = G * massa1 * massa2 / distancia ** 2
let forca = p5.Vector.sub(this.posicao, particula.posicao)
.setMag(G * massa / (distancia ** 2))
// Aplica a força
particula.aplicaForca(forca)
}
...
Esta parece complicada, mas se você pensar nela em termos de física, na verdade é surpreendentemente simples.
- A primeira linha apenas garante que ela não esteja tentando se comparar.
- A seguir, declaramos algumas variáveis.
- A segunda declaração if / else foi usada apenas para parar as forças quando as partículas estão colidindo. Isto não é 100% necessário, mas as coisas provavelmente se quebrarão se elas chegarem muito perto.
- A variável
forca
aponta da partícula afetada para a afetadora. - Em seguida, a magnitude é definida para a quantidade de força utilizando a lei da gravitação universal.
- Finalmente, a força é aplicada à partícula.
Finalmente, vamos escrever um pouco de código para atualizar as propriedades da partícula:
...
atualiza() {
let deltaVelocidade = p5.Vector.mult(this.aceleracao, deltaTime)
this.velocidade.set(this.velocidade.add(deltaVelocidade))
this.posicao.set(this.posicao.add(p5.Vector.mult(this.velocidade, deltaTime)))
this.aceleracao.set(0, 0)
}
...
Isto também pode parecer um pouco intimidante no início, mas se você dividir em partes, é muito mais fácil de entender.
- A primeira linha apenas calcula a mudança na aceleração.
deltaTime
é utilizado para manter as forças constantes, não importa quantos quadros por segundo você esteja recebendo. - A próxima linha adiciona a aceleração à velocidade.
- Então a velocidade é adicionada à posição, novamente utilizando
deltaTime
para manter constantes as mudanças. - Finalmente, a aceleração é zerada. A velocidade permanece por causa do momento.
Boa! Você configurou a classe Particula
! Agora você pode voltar para o desenho.js
e configurar o gerenciamento das suas partículas.
Gerenciando as Partículas
Abra o arquivo desenho.js
na barra lateral esquerda. Neste momento, o código deve estar parecido com este:
function setup () {
createCanvas(400, 400)
}
function draw () {
background(51, 51, 51)
}
A função setup()
é executada uma vez no início do programa para fazer qualquer configuração que você possa precisar. Nós a utilizamos para criar a tela utilizando createCanvas()
e criar uma tela de 400x400 pixels para que possamos usá-la.
A função draw()
é executada em cada frame do programa. Ela pode ser utilizada para atualizar a animação a cada frame. Nós a utilizaremos para realizar as atualizações físicas e desenhar as partículas para a tela.
Este arquivo é onde vamos escrever toda a lógica do programa, desde a criação das Partículas
que acabamos de programar, até a aplicação da física a elas e a animação das mesmas.
Primeiro, vamos criar o conjunto de partículas que estaremos exibindo. Em seguida, precisamos atualizá-las e exibi-las em cada frame dentro da draw()
.
Primeiro, precisamos definir algumas constantes e certificar um conjunto de partículas que estaremos exibindo. Acima da função setup()
, no início do arquivo, adicione:
// Constantes
const G = 6.67e-11
const ESCALA = 0.001
// Array para guardar partículas
let particulas = []
Na física, G
refere-se à Constante Gravitacional, que é 6,67 x 10-11 N⋅m2⋅kg-2 (que vamos escrever como 6,67e-11 no JavaScript). Esta constante é usada como base para todos os tipos de equações divertidas.
Agora, vamos montar nossa tela. Na função setup()
, acrescente:
function setup() {
createCanvas(400, 400)
// Itere para criar cada partícula
for (let i = 0; i < 10; i++) {
let x = random(0, width)
let y = random(0, height)
let massa = random(2e8, 1e9)
// Adicione a nova partícula para a lista
particulas.push(new Particula(x, y, massa))
}
}
Tudo o que estamos fazendo na função setup()
é criar um local aleatório na tela, bem como uma massa aleatória, para a partícula. Então, criamos uma nova Particula
com esses dados e a adicionamos à matriz de partículas que acabamos de criar.
Todos os blocos de construção estão no lugar. Finalmente, vamos utilizar a função draw()
para animar as partículas que criamos de acordo com as regras da física. Na função draw()
, acrescente:
function draw() {
// Define o fundo do canvas para um cinza escuro
background(51, 51, 51)
// Itere sobre todas as partículas duas vezes
for (const particulaA of particulas)
for (const particulaB of particulas)
if (particulaA !== particulaB) particulaA.fisica(particulaB)
// Itera sobre as partículas novamente
for (const particula of particulas) {
// Atualiza a partícula com a nova aceleração e velocidade
particula.atualiza()
// Desenha a partícula no canvas
particula.desenha()
}
}
- Primeiro, definimos o fundo de nossa tela para um cinza escuro
- Em seguida, fazemos o loop através do conjunto de partículas duas vezes. Se as duas partículas no loop forem diferentes, utilizamos a função
fisica()
que escrevemos anteriormente para aplicar a física a elas - Depois disso, fazemos um loop através da matriz de partículas novamente e atualizamos cada objeto
Particula
com os novos dados como resultado da execução dafisica
sobre as partículas. - E finalmente, para ver as mudanças, desenhamos a partícula.
Estamos passando por isso devagar e passo a passo, mas não se esqueça que, como todo esse código está na função desenho
, ele é executado dezenas (às vezes centenas) de vezes por segundo. É assim que é criada a animação suave que você verá em um segundo.
const G = 6.67e-11
const ESCALA = 0.001
// Array to store particles
let particulas = []
function setup() {
createCanvas(400, 400)
// Itere para criar cada partícula
for (let i = 0; i < 10; i++) {
let x = random(0, width)
let y = random(0, height)
let massa = random(2e8, 1e9)
// Adicione a nova partícula para a lista
particulas.push(new Particula(x, y, massa))
}
}
function draw() {
// Define o fundo do canvas para um cinza escuro
background(51, 51, 51)
// Itere sobre todas as partículas duas vezes
for (const particulaA of particulas)
for (const particulaB of particulas)
if (particulaA !== particulaB) particulaA.fisica(particulaB)
// Itera sobre as partículas novamente
for (const particula of particulas) {
// Atualiza a partícula com a nova aceleração e velocidade
particula.atualiza()
// Desenha a partícula no canvas
particula.desenha()
}
}
Você Acabou!
Clique no botão verde “Run” no topo da página. Você deve ver as partículas coloridas se moverem!
E com isso, agora você deve ter uma simulação - embora muito básica - da gravidade entre partículas. Esta simulação é mais ou menos o que aconteceria na vida real com estas massas e distâncias. Os valores em si são um pouco exagerados para serem visualmente mais interessantes.
O que fazer a seguir?
Tente mudar alguns valores e veja como eles afetam a simulação. Adicione algumas características extras como criar uma partícula com um clique, antipartículas, ou talvez aplicar este conceito de uma maneira totalmente diferente?
- Adicionar no Clique: Adicione uma partícula sempre que você clicar na tela
- Anti-Partículas: Anti-partículas que repelem outras partículas em vez de atrair
- Texto Físico: Letras de traços com partículas
Agora que você terminou de construir este maravilhoso projeto, compartilhe sua bela criação com outras pessoas! Lembre-se, é só mandar a URL do seu projeto!
Você provavelmente conhece as melhores maneiras de entrar em contato com seus amigos e familiares, mas se você quiser compartilhar seu projeto com a comunidade brasileira do Hack Club, não há melhor lugar para fazer isso do que no Discord do Hack Club Brasil.✨
- Clique aqui para fazer parte da nossa comunidade!
- Depois, poste o link do seu projeto no canal
💡┇criações
para compartilhá-lo com todos os Hack Clubbers!
A comunidade te espera!🎉🎉