오늘은 노트르담 대성당의 종을 울려 보도록 하겠습니다~^^*
일요일 동네 교회의 첨탑에서 뎅~뎅~ 울리던 종소리를 떠올리며,
콰지모토가 매일 긴 줄을 붙잡고 정성으로 울렸던 노트르담 대성당 종을 프로그램으로 표현해 보겠습니다~^^*
아래 동영상 강의를 클릭하시면, 오늘 공부 내용을 바로 시작(27:10)하실 수 있어요~^^* 29:00까지 보시면 오늘 공부 끝~~^^*
음… 우리가 줄을 잡아당겨도, 첨탑 지지대에 매달려 있는 종은 지지대에서 떨어지면 안되는데요….음….떨어지면 머리가 “아야”해요….떨어지면 안돼요….!!!
어쩌면 좋을까요?
아하!!!
캔버스 화면 제일 위에 위치하는 입자 즉 입자 배열 첫 번째 요소, particles[0]는 꼼짝하지 않도록 만들면 될 것 같아요~~!!!
입자가 particles[0]이면 움직이지 않도록 하고, 입자가 partcicles[0]가 아니면 움직이도록 하면 어떨까요~~!!
입자 클래스에, 잠김상태를 알려주는 Boolean Logic을 사용해 보겠습니다.
particles[0]는 잠김 상태가 True이다라고 설정을 하고, 다른 입자들은 잠김 상태가 False이다라고 설정을 해 볼게요.
그리고, 잠김 상태가 True가 아니라면 움직이도록 하겠습니다.
즉, particle[0]을 제외한 다른 입자들이 움직이게 되겠지요~^^*?
아래의 알고리즘을 살펴 보셔도 좋겠지요~^^*?
class Particle{
constructor(x,y){
(1) this.locked = false;
...
}
update(){
(3) if(!this.locked){
움직인다;
}
}
....
function setup(){
...
(2) particles[0].locked = true;
...
}
//(1) 모든 입자들은 기본적으로 locked의 Boolean 값을 false로 설정해 놓습니다.
//(2) 입자 배열[0], 즉 캔버스 화면 맨 위에 있는 매듭은 locked의 Boolean 값을 true로 설정합니다.
//(3) locked의 Boolean 값이 false인 입자만 움직입니다.
// "!"는 Not의 의미를 가지고 있습니다. (Not + locked) 가 True 일 때, 움직입니다. 다시 말해서, (Not + False) = True 일 때 움직입니다. 그러므로, locked의 Boolean 값이 false일 때 움직입니다.
// particles[0]는 Boolean 값이 true이므로, (!locked) = (Not True) = (False)가 되어, 조건을 만족시키지 않기 때문에, 움직이지 못합니다.
이제 전체 코드를 살펴 볼까요~~^^*?
자세한 내용은 주석을 참고해 주시면 감사하겠습니다~^^*
class Particle{
constructor(x,y){
this.locked = false;
//Boolean Logic을 사용해 보겠습니다.
//입자들은 기본적으로 locked되지 않은 상태로 생성하겠습니다.
//this.locked 상태는 false로 기본설정하겠습니다.
this.acceleration = createVector(0,0);
this.velocity = createVector(0,0);
this.position = createVector(x,y);
this.mass = 1;
}
applyForce(force){
let f = force.copy();
f.div(this.mass);
this.acceleration.add(f);
// F = M * A를 적용하여, A = F / M 관계를 사용합니다.
// f = F / M 하여, f를 가속도에 더합니다.
}
update() {
if(!this.locked)
//this.locked가 false이면, !this.locked는 true가 되어요.
//그러면 아래의 움직임 명령어들을 수행해요. 움직여요.
//this.locked가 true이면, !this.locked는 false가 되어요.
//그러면, 아래의 움직임 명령어들을 수행하지 않아요. 가만히 있어요.
{
this.velocity.mult(0.99);
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
// 공기저항을 반영하여 속도를 점차 줄여요.
// 가속도가 속도에 영향을 주고
// 속도가 위치에 영향을 준 뒤
// 가속도는 0로 재세팅합니다.
// 함수 draw()는 무한반복하기 때문에, 가속도가 force의 변화가 없는데도 중첩증가하지 않는 장치가 필요합니다: 가속도 재세팅!
}
}
show(){
stroke(255);
strokeWeight(4);
fill(127, 255, 127);
ellipse(this.position.x, this.position.y, 12);
//종에 연결된 줄의 매듭을 표현하기 위해 동그라미는 작게 그리겠습니다.
}
}
class Spring {
constructor(k, restLength, a, b){
this.k = k;
this.restLength = restLength;
this.a = a;
this.b = b;
}
update(){
let force = p5.Vector.sub(this.b.position, this.a.position);
// 벡터 b 입자에서 벡터 a 입자를 뺍니다.
// 벡터 force의 방향은 입자 a에서 입자 b으로 향합니다.
let x = force.mag() - this.restLength;
force.normalize();
force.mult(this.k*x);
this.a.applyForce(force);
// 벡터 force의 방향이 이미 입자 a에서 입자 b로 향하고 있으므로, 탄성계수 k * 거리 x를 그대로 씁니다.
// 입자 a가 입자 b로 향하는 힘입니다.
force.mult(-1);
this.b.applyForce(force);
//입자 b가 입자 a로 향하는 힘을 표현하기 위해서, 벡터 force에 -1을 곱하여, 힘의 방향을 바꿉니다.
// 두 입자 a, b가 스프링으로 연결되어 서로를 향한 힘을 가지고 있습니다.
}
show(){
strokeWeight(4);
stroke(255);
line(this.a.position.x, this.a.position.y, this.b.position.x, this.b.position.y);
}
}
let particles = [];
let springs = [];
let k = 0.01;
let gravity;
let spacing = 10;
//처음 시작할 때, 입자들 사이의 수직 거리가 될 것입니다~^^*
//입자들 사이의 스프링의 원래 길이(restLength)로 사용하겠습니다~^^*
function setup() {
createCanvas(400, 400);
for( let i = 0; i <10; i++){
// [0] ~ [9] 총 10개의 입자를 만들어 보겠습니다~^^*
particles[i] = new Particle(200, i*spacing);
// 화면 가운데, 맨 위에서 아래로 spacing = 10 간격으로 수직으로 나란히 위치시켜 볼게요~^^*
if( i != 0){
// particles[0] 즉 (200, 0)에 있는 입자에 대해서는 아래의 일을 하지 않습니다. 왜냐하면, 이 입자 앞에는 그 어떤 입자도 없기 때문입니다.
// parcicles[0]는 배열 맨 처음의 요소라서, 자신 앞에 다른 요소를 가지고 있지 않아 연결 작업이 무의미 합니다~
let a = particles[i];
let b = particles[i - 1];
let spring = new Spring(k, spacing, a, b);
//요소 particles[i]는 자신 바로 앞에 있는 요소 particles[i-1]과 스프링으로 연결됩니다.~^^*
springs.push(spring);
//생성된 spring을 배열 springs[]에 저장합니다.
//배열 springs[]는 배열 particles[]보다 요소가 1개 적게 되겠네요? (1->0), (2->1), (3->2), (4->3) ...,(9->8) 총 9개의 spring이 생성되어 배열 springs[]에 저장됩니다~^^*
}
}
particles[0].locked = true;
//캔버스 화면 제일 위에 있는 입자, 즉 입자 배열 첫번째 요소인 particles[0]의 클래스 구성요소인 locked 의 Boolean 상태를 기본값 false 에서 true 로 바꾸겠습니다~^^*
gravity = createVector(0, 0.01);
//줄의 매듭들이 중력의 영향을 받아 차르르 예쁘게 자리잡도록 해보겠습니다~^^*
}
function draw() {
background(220);
for( let s of springs){
s.update();
s.show();
}
//모든 스프링의 움직임을 보여주고
for ( let p of particles){
p.applyForce(gravity);
p.update();
p.show();
}
//모든 줄 매듭의 움직임을 보여줍니다.
let tail = particles[particles.length - 1];
//배열 맨마지막 요소를 변수 tail에 저장하겠습니다~
if (mouseIsPressed) {
tail.position.set(mouseX, mouseY);
tail.velocity.set(0,0);
}
//마우스/손가락을 클릭/눌러서 노트르담 대성당의 종을 울려보세요.
//줄이 부드럽게 움직이나요~^^*
}
이제 콰지모토가 되어 노트르담 대성당의 종을 정성을 담아 울려 볼까요~~^^* 뎅~뎅~뎅~^^*
오늘은 비가 온 뒤라 제법 햇살도 부드럽고 해서 걸어다니기 좋았어요~^^*
콰지모토가 평생을 종탑 안에 살면서, 종탑 아래 바깥 세상을 참 많이 걸어보고 싶었을 것 같은데요…. 자유롭게 걸어 다닐 수 있음이 새삼 감사했어요~^^*
콰지모토가 종탑 아래 바깥 세상을 꿈꾸며 노래 부르는 심정을 다양한 방식으로 절절하게 표현해 볼 수 있을 것 같은데요. 친근한 애니메이션으로, 수화를 곁들여 두 명의 배우가 함께, 또는 한 명의 배우가 간결하면서도 상징적인 분장을 하고서, 콰지모토의 심경을 표현할 수도 있네요…
우리도, COVID-19 시절 바깥 세상에 자유롭게 나가 사람들과 무언가를 함께 할 수 있기를 많이 바라기도 했었지요. 그 시절, 물리적으로 함께 할 수 없지만, 온라인으로 함께 만나 아름다운 공연을 해 낸 배우들도 계셨네요~^^*
COVID-19으로 인해, 콰지모토의 심정을 함께 느껴보았던 우리라서, 콰지모토가 지금은 천국의 친구들과 자유롭게 바깥 나들이를 하며 함께 어울리고 있기를 바라게 되는 것 같아요. 이제 콰지모토는 천국에서 친구들과 함께 즐겁게 종을 울리고 있겠지요~^^*
콰지모토의 이야기를 다양한 방식으로 표현할 수 있듯이, 어떤 이야기를 코딩을 통해서도 다양하게 표현할 수 있는 역량을 기르고 싶다는 소망을 가지게 되는 오늘입니다.
콰지모토는 천국에서 지금 행복하고, 우리는 여기에서 날마다 성장하기를 바라게 되네요.
오늘도 저와 함께 코딩 공부를 해주셔서 감사합니다~^^*
내일은 우리 음악 함께 들어요~~^^*
넵!!! 꿈은 이루어 집니다!!!
댓글 남기기