Silverback9

#야생으로

Creative Coding 독학 제178일 2024년09월21일(토)

어제 비눗방울 만들기 놀이 재미있었지요~~^^* 샴푸의 요정님~~^^*

샴푸의 요정인 우리들이 만들어 내는 비눗방울들이 지붕에서 흘러 내려 땅 아래로 내려가면~, 어머나!

하수관이 비눗방울로 점점 채워지게 되겠네요…

하수관 속에서 비눗방울들의 공기가 빠져서 흐르는 물에 잘 녹아들어야 할텐데요…

그렇지 않으면, 하수관이 비눗방울로 빡빡하게 채워져 제 역할을 못하게 될 수도 있을 것 같아요!

어머 그러면 안돼요!!

그래서~~!!

오늘은 컴퓨터의 업무량을 줄여주는 작업을 해 보면 어떨까요~^^*

뭔가 익숙한 이 느낌????

그렇죠!!

화면 아래로 흘러나간 비눗방울을 비눗방울 배열에서 제거하는 거예요~~^^*

비눗방울이 화면 아래로, 음…넉넉히 100 pixel 정도 아래로, 흘러 내려가면~^^*

화면 밖으로 나갔다는 시그널을 보내기로 할까요?

isOffScreen() {
        let pos = this.body.position;
        return (pos.y > height + 100)
}
//비눗방울의 y 좌표가 캔버스 바닥보다 100 pixel 아래에 있으면, true 값을 return하도록 할게요~^^*
//그래픽 화면에서, y축 값의 증가는 아래로 내려간다는 의미였지요?^^* 

(1) 화면 밖으로 나갔다는 시그널을 확인하면~

if (circles[i].isOffScreen()) 

(2) matter.js의 world에서 제거를 하고~

removeFromWorld() {
        Composite.remove(world, this.body);
} 
circles[i].removeFromWorld();

(3) p5.js의 비눗방울 배열에서도 제거를 하고~

circles.splice(i, 1);

(4) 비눗방울 배열의 인덱스를 한 칸 앞으로 되돌릴게요~

 i--;

그런데 Why?

Because~ 배열에서 한 요소를 제거한다는 것은… 음… 아날로그 영화 필름의 한 컷을 잘라내고, 그 뒷부분 필름조각을 그 앞부분 끝에 붙이는 것과 비슷할 것 같아요…^^*

배열에서 제거되는 요소의 뒤에 있던 요소가 제거된 요소의 자리로 옮겨지게 되어요. 그 뒤의 요소들도 다 한 칸씩 앞으로 이동하게 되는 효과가 생기겠네요.

그래서~~^^*, 배열에서 요소 하나를 제거하면, 배열의 인덱스를 한 칸 앞으로 줄여야, 제거된 요소 뒤에 있다가 앞으로 한 칸 옮겨진 요소를 빠뜨리지 않고 체크할 수 있어요~~^^*

근데요…같은 자리를 한 번 더 체크하는데 왜 인덱스를 하나 줄여야 되나요?

아!! 그건요!! for loop 때문인 것 같아요~~!!!

for (let i = 0; i < circles.length; i++) {
.
.
.
i--;
}

for loop를 한 번 돌 때마다 index가 하나 증가하기 때문에, 미리 하나 줄여 놓으면, 결국 같은 index자리를 한 번 더 점검하는 효과가 있겠네요~^^*

네~~^^* 그래서~~^^* 배열에서 요소 하나를 제거하면, 다음 요소들이 한 칸씩 앞으로 당겨지기 때문에, 제거된 요소 자리로 옮겨진 요소를 놓치지 않기 위해, index를 하나 줄인 후 for loop를 돌려야 할 것 같네요.

이 네 단계를 정리해 보면~~^^*

isOffScreen() {
        let pos = this.body.position;
        return (pos.y > height + 100)
 }

removeFromWorld() {
        Composite.remove(world, this.body);
} 
 for (let i = 0; i < circles.length; i++) {
        circles[i].show();
        if (circles[i].isOffScreen()) {
            circles[i].removeFromWorld();
            circles.splice(i, 1);
            i--;
        }
 }

아하!

하나. 우리가 matter.js로 동그라미를 만들고 p5.js로 이 동그라미를 시각화하기 때문에, matter.js의 World와 p5.js의 배열 모두에서, 제거해야 하는군요!

둘. 배열에서 제거하면, 다음 요소들이 한 칸씩 앞으로 이동하기 때문에, index를 1 줄인 후~ for loop를 돌려 index가 1 증가하면~ 원래의 index 값이 되도록 하여~ 제거된 요소 자리로 한 칸 옮겨진 요소를 놓치지 않고 체크해야 하는군요! i – 1 + 1 = i ~~^^*

이제 그럼 지붕을 흘러 내려 땅 아래 하수구로 들어간 비눗방울을 쏙쏙 녹여서, 하수구의 업무 부담을 줄여주는 프로그램의 전체 코드를 저와 함께 살펴 보시죠!!!

동그라미 갯수 확인용이니, 동그라미는 자동 생성하도록 할게요~^^*

<!DOCTYPE html>
<html lang="en">
  <head>
    https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js
    https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/addons/p5.sound.min.js
    https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    http://sketch.js
    http://circle.js
    http://boundary.js
  </body>
</html>
class Boundary {
    constructor(x, y, w, h, a) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        let options = {
            friction: 0,
            restitution: 0.6,
            angle: a,
            isStatic: true
        }
        this.body = Bodies.rectangle(this.x, this.y, this.w, this.h, options);
        Composite.add(world, this.body);
    }

    show() {
        let pos = this.body.position;
        let angle = this.body.angle;
        push();
        translate(pos.x, pos.y);
        rotate(angle);
        rectMode(CENTER);
        fill(0);
        rect(0, 0, this.w, this.h);
        pop();
    }
}
class Circle {
    constructor(x, y, r) {
        this.x = x;
        this.y = y;
        this.r = r;
        let options = {
            friction: 0,
            restitution: 0.6
        }
        this.body = Bodies.circle(this.x, this.y, this.r, options);
        Composite.add(world, this.body);
    }

    isOffScreen() {
        let pos = this.body.position;
        return (pos.y > height + 100)
    }

    removeFromWorld() {
        Composite.remove(world, this.body)
    }
    
    show() {
        let pos = this.body.position;
        let angle = this.body.angle;
        push();
        translate(pos.x, pos.y);
        rotate(angle);
        rectMode(CENTER);
        strokeWeight(1);
        stroke(255)
        fill(127);
        ellipse(0, 0, this.r * 2);
        pop();
    }
}
const { Engine, World, Bodies, Composite } = Matter;

let engine;
let world;
let circles = [];
let boundaries = [];

function setup() {
    createCanvas(400, 400);
    engine = Engine.create();
    world = engine.world;
    boundaries.push(new Boundary(150, 200, width* 0.6, 20, 0.3));
    boundaries.push(new Boundary(250, 300, width* 0.6, 20, -0.3));
}
    
function draw() {
    background(51);
    Engine.update(engine);
    circles.push(new Circle(200, 50, random(5, 10)));
    //동그라미를 자동 생성해 보도록 하겠습니다~!
    for (let i = 0; i < circles.length; i++) {
        circles[i].show();
        if (circles[i].isOffScreen()) {
            circles[i].removeFromWorld();
            circles.splice(i, 1);
            i--;
        }
    }
    for (let i = 0; i < boundaries.length; i++) {
        boundaries[i].show();
    }
    console.log(circles.length, world.bodies.length);
    //p5.js의 동그라미 배열 속 요소의 갯수와 
    //matter.js의 world 안의 동그라미 body의 갯수를
    //console 창에서 확인해 볼까요? 
    //화면 아래로 내려가면 제거되어서, 
    //전체 갯수가 적절하게 유지되고 있을까요? 
}

동그라미 갯수를 확인해 봅시다~ 돋보기를 들고 우리 컴퓨터 화면 곳곳을 꼼꼼하게 점검해 볼까요~^^* 콘솔창의 두 수치의 변화도 관찰해 보시면 재미있겠지요~~^^*

오늘도 저와 함께 동그라미 갯수를 조절하여 컴퓨터의 업무를 덜어주는 프로그램을 완성해 주셔서 감사합니다~^^*

음…그런데…오늘 영화 필름 편집 이야기로 배열 요소 제거 설명을 하다보니, 세상의 변화가 좀 실감이 났어요…

만져지지 않는 가상의 정보를 실상의 물체로 느끼는 것에 어느덧 익숙해진 우리는~~^^*

그녀의 진지한 질문이 왠지 유머러스하다고 느끼게 되었습니다~^^*

손으로 필름을 만지던 옛날~~^^*

시간의 먼지를 닦아내고 아날로그 필름 편집의 기억을 들여다 보는 것도 재미있을 것 같아요~~^^*

옛날 영화들을 보면서 새로운 감회를 느끼는 토요일의 망중한을 선물드려요~^^*

오늘도 좋은 아침! 멋진 하루 보내셔요~^^*

내일 우리 또 만나서 코딩 공부 계속 해 나갈까요~~^^*

네~~^^* 꿈은 이루어 집니다~~^^*

댓글 남기기