์ ์ ํจ์ vs. ์ธ์คํด์ค ๋ฉ์๋
์๊ณ ๋ฆฌ์ฆ #3 (๋ง์ฐ์ค๋ฅผ ๋ฐ๋ผ ๊ฐ์)์ ์ดํด๋ณด๊ธฐ ์ ์ ๋ฒกํฐ์ PVector๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๋ ํ์ํ ์ค์ํ ๊ฐ๋ ์ ์ดํดํด์ผ ํ ํ์๊ฐ ์๋ค. ์ ์ (static) ๋ฉ์๋์ ์ธ์คํด์ค(instance) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ฌด์จ ์ฐจ์ด๊ฐ ์๋์ง ์์๋ณด์.
x ๊ฐ์ 0์ด๊ณ y๋ฅผ ๋ํด x๋ 5๊ฐ๋๋ ์ฝ๋๋ฅผ PVector ๊ธฐ๋ฐ์ผ๋ก ๋ณ๊ฒฝํ๋ฉด ์๋์ ๊ฐ๋ค. ๋ฒกํฐ v์ ๊ฐ์ (0,0)์ด๊ณ v์ u๋ฅผ ๋ํด v๋ (4,5)๊ฐ ๋๋ค.
var x = 0;
var y = 5;
x = x + y;
// ↓↓↓
// P ๋ฒกํฐ ๊ธฐ๋ฐ์ผ๋ก ๋ณ๊ฒฝ
var v = new PVector(0,0);
var u = new PVector(4,5);
v.add(u);
๋ ๋ค๋ฅธ ์์์ธ ์๋ ์ฝ๋๋ ์ฌ๋ฐ๋ฅด๊ฒ ์์ ํ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์์ง๋ง ์ฌ๋ฐ๋ฅด์ง ์๋ค. PVector ๊ฐ์ฒด๋ ์ด๋ ๊ฒ ์ธ ์ ์๋ค.
var x = 0;
var y = 5;
var z = x + y;
// ↓↓↓
// ์๋์ ๊ฐ์ด ๋ณ๊ฒฝํ๋ ๊ฒ์ ์ฌ๋ฐ๋ฅด์ง ์๋ค
var v = new PVector(0,0);
var u = new PVector(4,5);
var w = v.add(u); // ์์ง๋ง์ธ์. ์ด ์ฝ๋๋ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค!!!
add() ํจ์๋ฅผ ์ดํด๋ณด๋ฉด ์์์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ ํ ์ ์๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
PVector.prototype.add = function(v) {
this.x = this.x + v.x;
this.y = this.y + v.y;
};
add() ํจ์๋ ํธ์ถ๋ ๋ฒกํฐ๊ฐ์ ๋ณ๊ฒฝํ์ง๋ง ์๋ก์ด ๋ฒกํฐ๊ฐ์ returnํ์ง ์๋๋ค. ๋ ๊ฐ์ PVector ๊ฐ์ฒด๋ฅผ ๋ํ ํ ๊ฒฐ๊ณผ๋ก ์๋ก์ด PVector๋ฅผ ๋ฐํํ๊ธฐ ์ํด์๋ ๋ฐ๋์ "์ ์ ์ธ" add() ํจ์๋ฅผ ์ด์ฉํด์ผ ํ๋ค.
์ ์ ํจ์๋ ๊ฐ์ฒด์ ๋ํด ์ ์๋๋ ํจ์์ง๋ง ๊ฐ์ฒด์ ํ๋กํผํฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์๋๋ค. ์ ์ ํจ์๋ ๊ฐ์ฒด๋ฅผ ๋ช ์นญ๊ณต๊ฐ(namespace) ์ฒ๋ผ ๋ค๋ฃฌ๋ค.
์๋๋ add() ์ธ์คํด์ค ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค. v๋ฅผ ์์ ํ๋ฏ๋ก ๋ฐํ ๊ฐ์ ์ ์ฅํ ํ์๊ฐ ์๋ค.
v.add(u);
๋ค์์ add() ์ ์ ํจ์๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค. ๋ง์ฝ ํด๋น ํจ์์ ๊ฒฐ๊ณผ๋ฅผ ๋ณ์์ ์ ์ฅํ์ง ์์ผ๋ฉด ์ด ์ฝ๋๋ ์ธ๋ชจ๊ฐ ์๋ค. ์ ์ ํจ์๋ ๊ฐ์ฒด ์์ฒด๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. PVector์ ์ ์ ํจ์๋ฅผ ์ด์ฉํ๋ฉด PVectors ์ ๋ ฅ๊ฐ ์ผ๋ถ๋ฅผ ๋ณํํ์ง ์๊ณ PVector ๊ฐ์ฒด์ ๋ํ ์ผ๋ฐ์ ์ธ ์ํ ์ฐ์ฐ์ ์ํํ ์ ์๋ค.
var w = PVector.add(v, u);
๋ค์์ add()๋ฅผ ์ ์ ํจ์๋ก ๋ง๋๋ ๋ฐฉ๋ฒ์ด๋ค.
PVector.add = function(v1, v2) {
var v3 = new PVector(v1.x + v2.x, v1.y + v2.y);
return v3;
};
์ฌ๊ธฐ์๋ ์ฌ๋ฌ ๊ฐ์ง ์ฐจ์ด์ ์ด ์๋ค.
- ๊ฐ์ฒด์ ํ๋กํ ํ์ ์ด ์๋ ๊ฐ์ฒด์ ๋ํด ์ง์ ์ ์ผ๋ก ํจ์๋ฅผ ์ ์ํ๋ค.
- ํจ์ ๋ด๋ถ์ this ํค์๋์ ์ ๋๋ก ์ ๊ทผํ์ง ์๋๋ค.
- ํจ์๋ก๋ถํฐ ๊ฐ์ ๋ฐํํ๋ค.
* ์ ์ ํจ์
๊ฐ์ฒด ์์ฑ์์ด ํด๋์ค๋ช .๋ฉ์๋๋ช ์ผ๋ก ํธ์ถ์ด ๊ฐ๋ฅํ๋ค.
* ์ธ์คํดํธ ๋ฉ์๋
๋ฐ๋์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ํ์ ํธ์ถ์ด ๊ฐ๋ฅํ๋ค. ์ธ์คํด์ค ์์ฑ ํ ์ฐธ์กฐ๋ณ์.๋ฉ์๋๋ช ์ผ๋ก ํธ์ถํ๋ค.
์ธํฐ๋ํฐ๋ธ ๋ฒกํฐ ๋ชจ์ - ๋ง์ฐ์ค ๋ฐฉํฅ์ผ๋ก์ ๊ฐ์๋
์๊ณ ๋ฆฌ์ฆ #3์์ ์ ํ ๊ฐ์ฒด๊ฐ ๋ง์ฐ์ค ์ชฝ์ผ๋ก ๊ฐ์ ํ๋ ๊ท์น์ ๋ฐ๋ผ, ๊ฐ์ฒด์ ๊ฐ์๋๋ฅผ ๋์ ์ผ๋ก ๊ณ์ฐํ๋ ์ฝ๋๋ฅผ ๋ง๋ค์ด ๋ณด์.
์ธ์ ๋ ๊ท์น์ด๋ ๊ณต์์ ๋ฐ๋ผ ๋ฒกํฐ๋ฅผ ๊ณ์ฐํ๋ ค๋ฉด ๋ ๊ฐ์ง, ์ฆ ํฌ๊ธฐ์ ๋ฐฉํฅ์ ๊ณ์ฐํด์ผ ํ๋ค. ๊ฐ์๋ ๋ฒกํฐ์ ๋ฐฉํฅ์ ๊ฐ์ฒด ์์น์์ ๋ง์ฐ์ค ์์น๋ก์ ๋ฐฉํฅ์ด๋ค.
๊ทธ๋ฆผ์ ๋ง์ฐ์ค์ ์์น์์ ๊ฐ์ฒด์ ์์น๋ฅผ ๋นผ์ ๋ฒกํฐ (dx, dy)๋ฅผ ๊ตฌํ ์ ์๋ค.
let dx = mouseX - x
let dy = mouseY - y
PVector๋ฅผ ์ด์ฉํ์ฌ ์ ์์์ ๋ค์ ์จ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
// ๊ฐ์ฒด์ ์์น์์ ๋ง์ฐ์ค ์์น๋ฅผ ๊ฐ๋ฆฌํค๋ PVector
const mouse = new PVector(mouseX, mouseY);
// ์์ ํ ์๋ก์ด PVector๋ฅผ ์ํ๊ธฐ ๋๋ฌธ์ ์ ์ sub()๋ฅผ ์ด์ฉ
const dir = PVector.sub(mouse, position);
์ด์ ๊ฐ์ฒด๊ฐ ๋ง์ฐ์ค ๋ฐฉํฅ์ผ๋ก ์ผ๋ง๋ ์ ์ํ๊ฒ ๊ฐ์ํ๋์ง๋ฅผ ๊ฒฐ์ ํด์ผ ํ๋ค.
๊ฐ์๋ PVector์ ํฌ๊ธฐ๋ฅผ ์ค์ ํ๋ ค๋ฉด ๋จผ์ ๋ฐฉํฅ ๋ฒกํฐ๋ฅผ ์ ๊ทํํด์ผ ํ๋ค. ๋ฒกํฐ๋ฅผ ๊ธธ์ด๊ฐ 1์ธ ๋จ์ ๋ฒกํฐ๋ก ์ถ์ํ๋ฉด ๋ฐฉํฅ์ ๋ํ๋ด๋ฉด์ ์ฝ๊ฒ ์์์ ๊ฐ๋งํผ ํฌ๊ธฐ๋ฅผ ํค์ธ ์ ์๊ฒ ๋๋ค.
์์ฝํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค:
- ๊ฐ์ฒด์์ ํ๊ฒ ์์น(๋ง์ฐ์ค)๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ฒกํฐ๋ฅผ ๊ณ์ฐ
- ํด๋น ๋ฒกํฐ๋ฅผ ์ ๊ทํ(๊ธธ์ด๋ฅผ 1๋ก ์ถ์)
- ํด๋น ๋ฒกํฐ์ ํน์ ๊ฐ์ ๊ณฑํ์ฌ ๋ฒกํฐ๋ฅผ ์ ์ ํ ๊ฐ์ผ๋ก ํ์ฅ
- ๊ทธ ๋ฒกํฐ๋ฅผ ๊ฐ์๋์ ํ ๋น
๋ค์์ ์ ๋จ๊ณ๋ฅผ ๋ชจ๋ ๊ตฌํํ ํ๋ก๊ทธ๋จ์ด๋ค.
<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
let mouseX = 0;
let mouseY = 0;
canvas.addEventListener('mousemove', function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
const PVector = function(x, y) {
this.x = x;
this.y = y;
};
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;
};
// ์ ์ sub ๋ฉ์๋
PVector.sub = function(v1, v2) {
const v3 = new PVector(v1.x - v2.x, v1.y - v2.y);
return v3;
};
const Mover = function() {
this.position = new PVector(canvas.width / 2, canvas.height / 2);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
};
Mover.prototype.update = function() {
// ๊ฐ์ฒด์ ์์น์์ ๋ง์ฐ์ค ์์น๋ฅผ ๊ฐ๋ฆฌํค๋ PVector
const mouse = new PVector(mouseX, mouseY);
const dir = PVector.sub(mouse, this.position);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
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;
}
};
const mover = new Mover();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
mover.update();
mover.checkEdges();
mover.display();
requestAnimationFrame(update);
};
update();
</script>
์์ด ๋์์ ๋๋ฌํ๋ฉด ์ ๋ฉ์ถ์ง ์์๊น? ์์ง์ด๋ ๊ฐ์ฒด๊ฐ ๋ฉ์ถฐ์ผ ํ ์์ ์ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค. ๊ฐ์ฒด๋ ์ค์ง ์ต๋ํ ๋น ๋ฅด๊ฒ ๋ชฉ์ ์ง๋ง์ ํฅํด ๊ฐ๋ค. ์ต๋ํ ๋น ๋ฅธ ์๋๋ก ๊ฐ๋ฉด ๋ถ๊ฐํผํ๊ฒ ๋ชฉ์ ์ง๋ฅผ ์ง๋์น๊ฒ ๋๊ณ ๋ค์ ๋์์ค๋ ๊ณผ์ ์ ๊ณ์ ๋ฐ๋ณตํ๊ฒ ๋๋ค. ๋์ค์ ๋ค๋ฅธ ๋จ์์์ ๊ฐ์ฒด๊ฐ ๋ชฉ์ ์ง์ ๋์ฐฉํ๋๋ก(์ฒ์ฒํ ์ ๊ทผํ๋๋ก ํจ) ํ๋ก๊ทธ๋๋ฐํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด๋๋ก ํ๊ฒ ๋ค.
์๋ ์์ ๋ ๊ฐ์ฒด์ ๋ฐฐ์ด์ ์ด์ฉํด์ ๋ง๋ค์ด ๋ณด์๋ค.
<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
// off-screen Canvas ์์ฑ
canvas.offCanvas = document.createElement('canvas');
canvas.offContext = canvas.offCanvas.getContext('2d');
canvas.offCanvas.width = 15;
canvas.offCanvas.height = 15;
canvas.offContext.beginPath();
canvas.offContext.ellipse(7.5, 7.5, 5, 5, 0, 0, Math.PI * 2, false);
canvas.offContext.lineWidth = 3;
canvas.offContext.strokeStyle = '#333333';
canvas.offContext.stroke();
canvas.offContext.fillStyle = '#ffffff';
canvas.offContext.fill();
let mouseX = 0;
let mouseY = 0;
canvas.addEventListener('mousemove', function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
const PVector = function(x, y) {
this.x = x;
this.y = y;
};
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;
};
PVector.prototype.mag = function() {
return Math.sqrt(this.x * this.x + this.y * this.y);
};
PVector.prototype.normalize = function() {
const m = this.mag();
if (m > 0) {
this.div(m);
}
};
// ์ ์ sub ๋ฉ์๋
PVector.sub = function(v1, v2) {
const v3 = new PVector(v1.x - v2.x, v1.y - v2.y);
return v3;
};
const Mover = function() {
this.position = new PVector(Math.random() * canvas.width, Math.random() * canvas.height);
this.velocity = new PVector(0, 0);
this.acceleration = new PVector(0, 0);
};
Mover.prototype.update = function() {
const mouse = new PVector(mouseX, mouseY);
const dir = PVector.sub(mouse, this.position);
dir.normalize();
dir.mult(0.2);
this.acceleration = dir;
this.velocity.add(this.acceleration);
this.velocity.limit(5);
this.position.add(this.velocity);
};
Mover.prototype.display = function() {
context.drawImage(canvas.offCanvas, this.position.x, this.position.y);
};
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 movers = [];
for (let i = 0; i < 20; i++) {
movers[i] = new Mover();
};
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < movers.length; i++) {
movers[i].update();
movers[i].display();
}
requestAnimationFrame(update);
};
update();
</script>
์ฐธ์กฐ ์ฌ์ดํธ
static๋ฉ์๋์ ์ธ์คํด์ค ๋ฉ์๋
https://jihyehwang09.github.io/2020/03/21/java-static-method-and-instance-method/
Canvas off-screen canvas
http://www.soen.kr/html5/html3/3-1-4.htm
https://studiomeal.com/archives/1097