|
| 1 | +--- |
| 2 | +title: 움직이는 볼 |
| 3 | +date: 2024-07-21 12:52:21 |
| 4 | +coverURL: |
| 5 | +--- |
| 6 | +<br /> |
| 7 | +<br /> |
| 8 | +<br /> |
| 9 | + |
| 10 | +<script src=" |
| 11 | +https://cdn.jsdelivr.net/npm/ [email protected]/lib/p5.min.js |
| 12 | +"></script> |
| 13 | + |
| 14 | + |
| 15 | +# 움직이는 볼 |
| 16 | + |
| 17 | +p5를 이용해 움직이는 볼을 그려보겠습니다. |
| 18 | +결과물은 아래와 같습니다. |
| 19 | + |
| 20 | +<div id="can"></div> |
| 21 | + |
| 22 | +<script> |
| 23 | + class Vec3 { |
| 24 | + constructor(x, y, r){ |
| 25 | + this.x = x; |
| 26 | + this.y = y; |
| 27 | + this.r = r; |
| 28 | + } |
| 29 | + } |
| 30 | + class Vec2 { |
| 31 | + constructor(x, y){ |
| 32 | + this.x = x; |
| 33 | + this.y = y; |
| 34 | + } |
| 35 | + } |
| 36 | + class Ball { |
| 37 | + constructor(p, v){ |
| 38 | + this.p = p; |
| 39 | + this.v = v; |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + |
| 44 | + let ball; |
| 45 | + |
| 46 | + function setup(){ |
| 47 | + const can = createCanvas(250, 250); |
| 48 | + can.parent('can'); |
| 49 | + ball = new Ball(new Vec3(10, 10, 20), new Vec2(90, 60)); |
| 50 | + } |
| 51 | + |
| 52 | + function draw(){ |
| 53 | + ball.p.x = ball.p.x + ball.v.x / 60; |
| 54 | + ball.p.y = ball.p.y + ball.v.y / 60; |
| 55 | + |
| 56 | + if(ball.p.x < 10 || ball.p.x > 240) { |
| 57 | + ball.v.x = -ball.v.x; |
| 58 | + } |
| 59 | + if(ball.p.y < 10 || ball.p.y > 240) { |
| 60 | + ball.v.y = -ball.v.y; |
| 61 | + } |
| 62 | + |
| 63 | + background(250); |
| 64 | + circle(ball.p.x, ball.p.y, ball.p.r); |
| 65 | + } |
| 66 | +</script> |
| 67 | + |
| 68 | + |
| 69 | +## 코드 |
| 70 | + |
| 71 | +> p5는 전역에 정의된 setup, draw 함수를 실행해 canvas를 만들고 |
| 72 | +그 위에 정의된 내용을 60 frame 단위로 draw 함수를 그려냅니다. |
| 73 | + |
| 74 | + |
| 75 | +```js |
| 76 | + class Vec3 { |
| 77 | + constructor(x, y, r){ |
| 78 | + this.x = x; |
| 79 | + this.y = y; |
| 80 | + this.r = r; |
| 81 | + } |
| 82 | + } |
| 83 | + class Vec2 { |
| 84 | + constructor(x, y){ |
| 85 | + this.x = x; |
| 86 | + this.y = y; |
| 87 | + } |
| 88 | + } |
| 89 | + class Ball { |
| 90 | + constructor(p, v){ |
| 91 | + this.p = p; |
| 92 | + this.v = v; |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + |
| 97 | + let ball; |
| 98 | + |
| 99 | + function setup(){ |
| 100 | + const can = createCanvas(250, 250); |
| 101 | + can.parent('can'); |
| 102 | + ball = new Ball(new Vec3(10, 10, 20), new Vec2(90, 60)); |
| 103 | + } |
| 104 | + |
| 105 | + function draw(){ |
| 106 | + ball.p.x = ball.p.x + ball.v.x / 60; |
| 107 | + ball.p.y = ball.p.y + ball.v.y / 60; |
| 108 | + |
| 109 | + if(ball.p.x < 10 || ball.p.x > 240) { |
| 110 | + ball.v.x = -ball.v.x; |
| 111 | + } |
| 112 | + if(ball.p.y < 10 || ball.p.y > 240) { |
| 113 | + ball.v.y = -ball.v.y; |
| 114 | + } |
| 115 | + |
| 116 | + background(250); |
| 117 | + circle(ball.p.x, ball.p.y, ball.p.r); |
| 118 | + } |
| 119 | +``` |
| 120 | + |
| 121 | + |
| 122 | +위 코드를 하나씩 뜯어보겠습니다. |
| 123 | + |
| 124 | +### 볼을 그리기 위한 위치 벡터 |
| 125 | + |
| 126 | +```js |
| 127 | + class Vec3 { |
| 128 | + constructor(x, y, r){ |
| 129 | + this.x = x; |
| 130 | + this.y = y; |
| 131 | + this.r = r; |
| 132 | + } |
| 133 | + } |
| 134 | +``` |
| 135 | + |
| 136 | +볼을 좌표평면 위에 그리기 위해서는 세가지 값이 필요합니다. |
| 137 | + |
| 138 | +그것은 바로 `(x, y, r)`입니다. |
| 139 | +x, y는 좌표값이며, r은 반지름입니다. |
| 140 | + |
| 141 | +이 값들을 p5에서 정의해놓은 circle 함수에 인자로 주어주면 |
| 142 | +canvas에 볼이 그려지게 됩니다. |
| 143 | + |
| 144 | +### 볼을 움직이기 위한 속도 벡터 |
| 145 | + |
| 146 | +```js |
| 147 | + class Vec2 { |
| 148 | + constructor(x, y){ |
| 149 | + this.x = x; |
| 150 | + this.y = y; |
| 151 | + } |
| 152 | + } |
| 153 | +``` |
| 154 | + |
| 155 | +볼을 그리기만하고 움직이지 못하면 안되겠습니다. |
| 156 | + |
| 157 | +볼을 움직이기 위해서는 벡터가 필요합니다. |
| 158 | +위에서 언급한대로 1 프레임마다 draw 함수가 재실행되므로 |
| 159 | + |
| 160 | +`ball.p.x`와 `ball.p.y`를 `ball.v.x`, `ball.v.y`만큼 이동시켜주면 되는 것입니다. |
| 161 | + |
| 162 | +```js |
| 163 | + function draw(){ |
| 164 | + ball.p.x += ball.v.x / 60; // vector 만큼의 거리를 60만큼 나눠서 |
| 165 | + ball.p.y += ball.v.y / 60; |
| 166 | + |
| 167 | + // if(ball.p.x < 10 || ball.p.x > 240) { |
| 168 | + // ball.v.x = -ball.v.x; |
| 169 | + // } |
| 170 | + // if(ball.p.y < 10 || ball.p.y > 240) { |
| 171 | + // ball.v.y = -ball.v.y; |
| 172 | + // } |
| 173 | + |
| 174 | + |
| 175 | + background(250); |
| 176 | + circle(ball.p.x, ball.p.y, ball.p.r); |
| 177 | + } |
| 178 | +``` |
| 179 | + |
| 180 | +### 볼이 벽에 부딪힌 경우 계산될 반사 벡터 |
| 181 | + |
| 182 | +볼이 벽에 부딪힌 경우, |
| 183 | + |
| 184 | +아래의 그래프로 설명할 수 있습니다. |
| 185 | + |
| 186 | +- u: 벽 |
| 187 | +- v: 공의 이동 벡터 |
| 188 | +- rv: 공의 이동 벡터에 대해 - 곱셈 |
| 189 | +- rvv: v + rvv |
| 190 | + |
| 191 | +와 같이 설명됩니다. |
| 192 | + |
| 193 | +<img src="/img/blog/p5/bounceball/bounce-ball.png" alt="vectors"> |
| 194 | + |
| 195 | +코드로는 아래와 같이 -를 곱해주면 됩니다. |
| 196 | + |
| 197 | +- 240은 캔버스의 크기 - 볼의 반지름 |
| 198 | +- 10은 볼의 반지름입니다. |
| 199 | + |
| 200 | +```js |
| 201 | + |
| 202 | + function draw(){ |
| 203 | + ball.p.x += ball.v.x / 60; // vector 만큼의 거리를 60만큼 나눠서 |
| 204 | + ball.p.y += ball.v.y / 60; |
| 205 | + |
| 206 | + if(ball.p.x < 10 || ball.p.x > 240) { |
| 207 | + ball.v.x = -ball.v.x; |
| 208 | + } |
| 209 | + if(ball.p.y < 10 || ball.p.y > 240) { |
| 210 | + ball.v.y = -ball.v.y; |
| 211 | + } |
| 212 | + |
| 213 | + background(250); |
| 214 | + circle(ball.p.x, ball.p.y, ball.p.r); |
| 215 | + } |
| 216 | +``` |
0 commit comments