๋ฒกํฐ ํฌ๊ธฐ์ ์ ๊ทํ
ํผํ๊ณ ๋ผ์ค์ ์ ๋ฆฌ ๊ณต์์ ํ์ฉํ๋ฉด ์ค์ ๋ฒกํฐ์ ๊ธธ์ด(ํฝ์ )๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
์ด๋ฅผ PVector ๊ฐ์ฒด์์ ๊ตฌํํ๊ธฐ ์ํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
PVector.prototype.mag = function() {
// sqrt : ์ ๊ณฑ๊ทผ์ ๋ฐํํ๋ ๋ฉ์๋
return sqrt(this.x * this.x + this.y * this.y);
};
๋ฒกํฐ์ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐํ ์ ์์ผ๋ฉด ์ ๊ทํ๋ ๊ฐ๋ฅํ๋ค. ์ ๊ทํ๋ ๋ฌด์ธ๊ฐ๋ฅผ ํ์คํ ์ํค๊ฑฐ๋ ๋ค๋ฅธ ๊ฒ๊ณผ ๋น๊ตํ๊ธฐ ์ฝ๋๋ก ๋ฐ๊พธ๋ ๊ฒ์ ์๋ฏธํ๋ค.
์ด๋ค ๋ฒกํฐ๋ฅผ ์ ๊ทํ(normalization) ์ํค๋ ค๋ฉด ๋ฒกํฐ์ ๋ฐฉํฅ์ ๊ทธ๋๋ก ๋๊ณ ํฌ๊ธฐ๋ฅผ 1๋ก ๋ฐ๊พธ์ด์ฃผ๋ฉด ๋๋ค. ํฌ๊ธฐ๊ฐ 1์ธ ๋ฒกํฐ๋ฅผ ๋จ์๋ฒกํฐ๋ผ๊ณ ํ๋ฉฐ, ๋จ์๋ฒกํฐ๋ ํฌ๊ธฐ์ ์๊ด์์ด ๋ฐฉํฅ์ ์ ์ํ๋ฏ๋ก ์ฌ๋ฌ๋ชจ๋ก ์ ์ฉํ๊ฒ ์ฐ์ธ๋ค.
PVector ๊ฐ์ฒด์์ ์ ๊ทํ ํจ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฉํ ์ ์๋ค.
PVector.prototype.normalize = function() {
const m = this.mag();
// ๋ฒกํฐ์ ํฌ๊ธฐ๊ฐ 0์ด ์๋๊ฒฝ์ฐ
if (m > 0) {
this.div(m);
}
};
๋ฒกํฐ ๋ชจ์ - ์๋
์ด์ ์ ๋ง๋ค์ด๋ณธ Bouncing Ball ์์ ์์ ๋ฒกํฐ๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด ๋ฌด์์ธ์ง ๋ณด์๋ค. ๋ ๋์๊ฐ ๊ฐ์ฒด ๋ด์์ PVector๋ฅผ ์ด๋ป๊ฒ ์ด์ฉํ๋์ง ์์๋ณด์.
์์ง์์ ์ฝ๋ฉ ๊ธฐ๋ณธ ์์น์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์์น์ ์๋๋ฅผ ๋ํ๋ค.
- ๊ทธ ์์น์ ๋ฌผ์ฒด๋ฅผ ๊ทธ๋ฆฐ๋ค.
Bouncing Ball ์์ ์์ ๋ชจ๋ ์ฝ๋๋ update ํจ์ ์์ ๋ค์ด๊ฐ๋ค. ์ด๋ฒ์๋ ์์ง์์ ๋ํ ๋ชจ๋ ๋ก์ง์ ๊ฐ์ฒด(object)๋ก ๊ฐ์ธ์ค ๊ฒ์ด๋ค.
ํ๋ฉด์์ ์์ง์ด๋ ์ด๋ค ๋ฌผ์ฒด๋ฅผ ๋ํ๋ด๋ ์ผ๋ฐ์ ์ธ Mover ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค. ์ด ๋ ๋ฐ๋์ ๋ค์๊ณผ ๊ฐ์ ๋ ์ง๋ฌธ์ ๊ณ ๋ คํด์ผ ํ๋ค.
- mover๋ ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๋์?
- mover๋ ์ด๋ค ๊ธฐ๋ฅ์ ๊ฐ์ง๋์?
-๋ฐ์ดํฐ : ์์์ ๋ณธ ์์ง์์ ์ฝ๋ฉ ๊ธฐ๋ณธ ์์น์ ์ํ๋ฉด, Mover ๊ฐ์ฒด์๋ ๋ ๊ฐ์ ๋ฐ์ดํฐ location๊ณผ velocity๊ฐ ์์ผ๋ฉฐ, ๋ ๋ค PVector ๊ฐ์ฒด๋ค.
const Mover = function() {
this.position = new PVector(Math.random() * canvas.width, Math.random() * canvas.height);
this.velocity = new PVector(Math.random(-2, 2), Math.random(-2, 2));
};
-๊ธฐ๋ฅ : Mover๋ ์์ง์ด๋ ๊ธฐ๋ฅ, ํ๋ฉด์ ๋ณด์ฌ์ผํ๋ ๊ธฐ๋ฅ, ๊ฐ์ฅ์๋ฆฌ์ ๋๋ฌํ์ ๋ ํ๋ ๊ธฐ๋ฅ์ด ํ์ํ๋ค.
Mover.prototype.update = function() {
this.position.add(this.velocity);
};
Mover.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, 20, 20, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#555555';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
};
Mover.prototype.checkEdges = function() {
if (this.position.x > canvas.width) {
this.position.x = 0;
}
else if (this.position.x < 0) {
this.position.x = canvas.width;
}
if (this.position.y > canvas.height) {
this.position.y = 0;
}
else if (this.position.y < 0) {
this.position.y = canvas.height;
}
};
์ด๋ฅผ ์ ์ฉํ ์์ ๋ ์๋์ ๊ฐ๋ค.
<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
// 2์ฐจ์ ๋ฒกํฐ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฉ์๋
const PVector = function(x, y) {
this.x = x;
this.y = y;
};
// ๋ฒกํฐ ๋ง์
๋ฉ์๋ (location = location + velocity)
PVector.prototype.add = function(v) {
this.y = this.y + v.y;
this.x = this.x + v.x;
};
// ์์ง์ด๋ ๋ํ์ ๋ํ๋ด๋ ๊ฐ์ฒด Mover
const Mover = function() {
this.position = new PVector(Math.random() * canvas.width, Math.random() * canvas.height);
this.velocity = new PVector(Math.random(-2, 2), Math.random(-2, 2));
};
// Mover์ ์์ง์๊ณผ ๊ด๋ จ๋ ๋ฉ์๋
Mover.prototype.update = function() {
this.position.add(this.velocity);
};
// Mover๋ฅผ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๋ฉ์๋
Mover.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, 20, 20, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#555555';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
};
// Mover๊ฐ ๊ฐ์ฅ์๋ฆฌ์ ๋๋ฌํ์ ๋ ๋ฉ์๋
Mover.prototype.checkEdges = function() {
if (this.position.x > canvas.width) {
this.position.x = 0;
}
else if (this.position.x < 0) {
this.position.x = canvas.width;
}
if (this.position.y > canvas.height) {
this.position.y = 0;
}
else if (this.position.y < 0) {
this.position.y = canvas.height;
}
};
const mover = new Mover();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
mover.update();
mover.checkEdges();
mover.display();
requestAnimationFrame(update);
};
update();
</script>
๋ฒกํฐ ๋ชจ์ - ๊ฐ์๋
๋ณด๋ค ์ฌ๋ฏธ์๋ ์์ง์ ์ฆ, ์ฃผ์์ ์ค์ ์ํฉ์์ ๋ณผ ์ ์๋ ์์ง์์ ๋ง๋ค๊ธฐ ์ํด Mover ๊ฐ์ฒด์ ๋ ํ๋์ PVector์ธ ๊ฐ์๋๋ฅผ ์ถ๊ฐํด๋ณด์.
์๋๋ ์์น์ ๋ณํ์จ๋ก ์ ์ํ๋ค๋ฉด, ์ฌ๊ธฐ์ ๋ฐฐ์ธ ๊ฐ์๋๋ ์๋์ ๋ณํ์จ๋ก ์ ์ํ๋ค.
velocity.add(acceleration);
position.add(velocity);
๋ฒกํฐ ๋ชจ์ - 1. ์ผ์ ํ ๊ฐ์๋
Mover ์์ฑ์์ ๊ฐ์๋๋ฅผ ๋ํ๋ผ ๋ ๋ค๋ฅธ PVector ํ๋กํผํฐ๋ฅผ ์ถ๊ฐํ๋ค. ์ผ์ ํ ๊ฐ์๋์ด๋ฏ๋ก ๊ฐ์ ๋ก ์ด๊ธฐํํ ํ ๊ณ์ ์ ์งํ๋ค.
*๊ฐ์๋ ๊ฐ์ ํฝ์ ๋จ์๋ก ์ธก์ ๋๋ ์ค์ผ์น ํ๋ ์ ๋น์จ์ ๋ฐ๋ผ ์ด๋น ์ฝ 30๋ฐฐ์ ๋ ์ฆ๊ฐํ๋ค. ๊ทธ๋์ ์๋ ๋ฒกํฐ์ ํฌ๊ธฐ๋ฅผ ํฉ๋ฆฌ์ ์ธ ๋ฒ์ ๋ด์์ ์ ์งํ๊ธฐ ์ํด ๊ฐ์๋ ๊ฐ์ ๊ฝค ์์ ๊ฐ์์ ์์ํ๊ณ ์ ์ง๋์ด์ผ ํ๋ค.
const Mover = function() {
this.position = new PVector(canvas.width / 2, canvas.height / 2);
this.velocity = new PVector(0, 0);
// ๊ฐ์๋ ๋ฐฑํฐ ๊ฐ์ฒด ์ถ๊ฐ
this.acceleration = new PVector(-0.001, 0.01);
};
๊ฐ์๋๋ก ์ธํด ์๋๊ฐ ์ ์ ๋์์ง๋ ๊ฒ์ update() ๋ฉ์๋์์ ์ํํ๋ค.
update๋ฅผ ๋ฐ๋ณตํ๋ฉด ์๋๊ฐ ๊ณ์ํด์ ์ฆ๊ฐํ๊ธฐ ๋๋ฌธ์ ํ๋ก๊ทธ๋จ์ ์ค๋ซ๋์ ์คํํ๋ฉด ์๋ ๊ฐ์ด ์์ฒญ๋๊ฒ ์ปค์ง ์ํ์ด ์๋ค. ๋ฐ๋ผ์ ์๋์ ์ต๋๊ฐ์ ๋์ด์ ์ ํํ ํ์๊ฐ ์๋ค.
Mover.prototype.update = function() {
// ์๋์ ๊ฐ์๋ ๋ฐฑํฐ ๋ง์
this.velocity.add(this.acceleration);
// ์๋ ์ ํ
this.velocity.limit(10);
this.position.add(this.velocity);
};
์ผ์ ํ ๊ฐ์๋๋ฅผ ๊ฐ๋ Bouncing Ball ์์ ๋ ๋ค์๊ณผ ๊ฐ๋ค.
<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
// 2์ฐจ์ ๋ฒกํฐ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฉ์๋
const PVector = function(x, y) {
this.x = x;
this.y = y;
};
// ๋ฒกํฐ ๋ง์
๋ฉ์๋ (location = location + velocity)
PVector.prototype.add = function(v) {
this.y = this.y + v.y;
this.x = this.x + v.x;
};
// ๋ฒกํฐ ๊ณฑ์
๋ฉ์๋
PVector.prototype.mult = function(n) {
this.x = this.x * n;
this.y = this.y * n;
};
// ๋ฒกํฐ ๋๋์
๋ฉ์๋
PVector.prototype.div = function(n) {
this.x = this.x / n;
this.y = this.y / n;
};
// ๋ฐฑํฐ ์ ๊ณฑ ๋ฉ์๋
PVector.prototype.magSq = function(v) {
return this.x * this.x + this.y * this.y;
};
// ๋ฐฑํฐ ํฌ๊ธฐ ์ ํ ๋ฉ์๋
PVector.prototype.limit = function(max) {
const mSq = this.magSq();
if (mSq > max * max) {
this.div(Math.sqrt(mSq));
this.mult(max);
}
return this;
};
// ์์ง์ด๋ ๋ํ์ ๋ํ๋ด๋ ๊ฐ์ฒด Mover
const Mover = function() {
this.position = new PVector(canvas.width / 2, canvas.height / 2);
this.velocity = new PVector(0, 0);
// ๊ฐ์๋ ๋ฐฑํฐ ๊ฐ์ฒด ์ถ๊ฐ
this.acceleration = new PVector(-0.001, 0.01);
};
// Mover์ ์์ง์๊ณผ ๊ด๋ จ๋ ๋ฉ์๋
Mover.prototype.update = function() {
// ์๋์ ๊ฐ์๋ ๋ฐฑํฐ ๋ง์
this.velocity.add(this.acceleration);
// ์๋ ์ ํ
this.velocity.limit(10);
this.position.add(this.velocity);
};
// Mover๋ฅผ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๋ฉ์๋
Mover.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, 20, 20, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#555555';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
};
// Mover๊ฐ ๊ฐ์ฅ์๋ฆฌ์ ๋๋ฌํ์ ๋ ๋ฉ์๋
Mover.prototype.checkEdges = function() {
if (this.position.x > canvas.width) {
this.position.x = 0;
}
else if (this.position.x < 0) {
this.position.x = canvas.width;
}
if (this.position.y > canvas.height) {
this.position.y = 0;
}
else if (this.position.y < 0) {
this.position.y = canvas.height;
}
};
const mover = new Mover();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
mover.update();
mover.checkEdges();
mover.display();
requestAnimationFrame(update);
};
update();
</script>
๋ฒกํฐ ๋ชจ์ - 2. ์์ ํ ์์์ ๊ฐ์๋
์์ ํ ์์์ ๊ฐ์๋๋ฅผ ๊ฐ์ง๊ธฐ ์ํด์๋ ๊ฐ์ฒด์ ์์ฑ์์์ ๊ฐ์๋๋ฅผ ์ด๊ธฐํํ๋ ๋์ ๊ฐ ์ฌ์ดํด ์ฆ, update()๊ฐ ํธ์ถ๋ ๋๋ง๋ค ์๋ก์ด ๊ฐ์๋๋ฅผ ์ ํํด์ผ ํ๋ค.
๋ฌด์์ ๋ฒกํฐ๋ ์ ๊ทํ๋ ๋ฒกํฐ์ด๊ธฐ ๋๋ฌธ์ ๋ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ํฌ๊ธฐ๋ฅผ ์กฐ์ ํ ์ ์๋ค.
- ๊ฐ์๋๋ฅผ ์์๊ฐ์ผ๋ก ์กฐ์
acceleration = PVector.random2D(); acceleration.mult(0.5);โ
- ๊ฐ์๋๋ฅผ ์์์ ๊ฐ์ผ๋ก ์กฐ์
acceleration = PVector.random2D(); acceleration.mult(random(2));โ
๋ช ํํด ๋ณด์ด์ง๋ง ๊ฐ์๋๊ฐ ๋จ์ํ ์์ง์ด๋ ๋ฌผ์ฒด์ ์๋๋ฅผ ๋น ๋ฅด๊ฒ ํ๊ฑฐ๋ ๋๋ฆฌ๊ฒํ๋ ๊ฒ์ด ์๋๋ผ ์๋์ ํฌ๊ธฐ๋ ๋ฐฉํฅ์ ๋ณํ๋ผ๋ ๊ฒ์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํ๋ค.
<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
// 2์ฐจ์ ๋ฒกํฐ ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฉ์๋
const PVector = function(x, y) {
this.x = x;
this.y = y;
};
// ๋ฒกํฐ ๋ง์
๋ฉ์๋ (location = location + velocity)
PVector.prototype.add = function(v) {
this.y = this.y + v.y;
this.x = this.x + v.x;
};
// ๋ฒกํฐ ๊ณฑ์
๋ฉ์๋
PVector.prototype.mult = function(n) {
this.x = this.x * n;
this.y = this.y * n;
};
// ๋ฒกํฐ ๋๋์
๋ฉ์๋
PVector.prototype.div = function(n) {
this.x = this.x / n;
this.y = this.y / n;
};
// ๋ฐฑํฐ ์ ๊ณฑ ํฌ๊ธฐ ๋ฉ์๋
PVector.prototype.magSq = function(v) {
return this.x * this.x + this.y * this.y;
};
// ๋ฐฑํฐ ํฌ๊ธฐ ์ ํ ๋ฉ์๋
PVector.prototype.limit = function(max) {
const mSq = this.magSq();
if (mSq > max * max) {
this.div(Math.sqrt(mSq));
this.mult(max);
}
return this;
};
// 2์ฐจ์ ๋ฒกํฐ ๋๋ค ๋ฐํ ๋ฉ์๋
PVector.random2D = function() {
return new PVector(
1 * Math.cos(Math.random() * Math.PI * 2),
1 * Math.sin(Math.random() * Math.PI * 2)
);
};
// ์์ง์ด๋ ๋ํ์ ๋ํ๋ด๋ ๊ฐ์ฒด Mover
const Mover = function() {
this.position = new PVector(canvas.width / 2, canvas.height / 2);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(-0.001, 0.01);
};
// Mover์ ์์ง์๊ณผ ๊ด๋ จ๋ ๋ฉ์๋
Mover.prototype.update = function() {
this.acceleration = PVector.random2D();
this.acceleration.mult(Math.random(2));
this.velocity.add(this.acceleration);
this.velocity.limit(10);
this.position.add(this.velocity);
};
// Mover๋ฅผ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๋ฉ์๋
Mover.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, 20, 20, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#555555';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
};
// Mover๊ฐ ๊ฐ์ฅ์๋ฆฌ์ ๋๋ฌํ์ ๋ ๋ฉ์๋
Mover.prototype.checkEdges = function() {
if (this.position.x > canvas.width) {
this.position.x = 0;
}
else if (this.position.x < 0) {
this.position.x = canvas.width;
}
if (this.position.y > canvas.height) {
this.position.y = 0;
}
else if (this.position.y < 0) {
this.position.y = canvas.height;
}
};
const mover = new Mover();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
mover.update();
mover.checkEdges();
mover.display();
requestAnimationFrame(update);
};
update();
</script>