π§ λ§μ°μ€λ‘ μλ°μ ν΄λ¦ν΄μ ν΄λΌμ΄λ°μ ν μ μλ κ²μμ λλ€.
- Preview
- Motivation
- Challenges
- Tech stack
- Feature
- Timeline
- Video
- Repository Link
- Memoir
ν΄λΌμ΄λ°, μν, 물리 - μ κ° μ’μνλ μμλ€μ λͺ¨μμ μμ΄λμ΄λ₯Ό κΈ°ννμ΅λλ€.
λ¨μν νΉμ λΌμ΄λΈλ¬λ¦¬ μ¬μ©λ²μ μλ κ²μ λ λμ, λͺ¨λκ° λ°°μ΄ μν,물리 곡μμ λ°νμΌλ‘ λ Όλ¦¬μ μΌλ‘ μ¬κ³ λ₯Ό μ κ°ν΄λκ°λ κ³Όμ μ 보μ¬μ£Όκ³ μΆμμ΅λλ€. μ¬λμ κ΄μ μμ§μ λ° λ¬Όλ¦¬μμ§ κ΅¬νμ΄λΌλ μμν λ¬Έμ μ λμ ν΄λ³΄κ³ μΆμμ΅λλ€.
ν΄λΌμ΄λ°μ§μ κ°μ§ μλλΌλ ν΄λΌμ΄λ° λμλ€μ μ°μ΅ν μ μλ μλΉμ€κ° μμΌλ©΄ μ’κ² λ€λ μκ°μ΄ λ€μμ΅λλ€.
κ·Έλμ μ΄ κ²μμ λ¨μν μ¬λ―Έλ‘λ§ νλκ² μλλΌ λ°°μΈ μ μ΄ μλλ‘ κΈ°ννμ΅λλ€.
- ν체 μμ§μμ μκ°νλ μ΅κ΄ κΈ°λ₯΄κΈ°
- μ€μ λ‘ λ¬΄κ²μ€μ¬μ μ¬λ¦΄ λλ νλ‘λ§ λμ΄μ¬λ¦¬λκ² μλλΌ, λ°μ λ¨Όμ μ¬λ¦¬κ³ ν체νμΌλ‘ 무κ²μ€μ¬μ μ¬λ¦¬λκ² μ²΄λ ₯ μλͺ¨ ν¨μ¨μ μμ΄μ μ’μ΅λλ€.
- λ£¨νΈ νμΈλ© (route finding) μ°μ΅νκΈ°
- λ£¨νΈ νμΈλ©: νλλ₯Ό 보면μ μ΄λ€ λμκ³Ό μμλ‘ νλλ₯Ό μ‘μμ§ νλ¨νλ κ²
κ° μ 체 λΆμλ₯Ό νλμ new Graphics()
κ°μ²΄λ‘ μμ±νμ΅λλ€.
// src/utils/player.js
export const body = new Graphics();
const leftUpperArm = new Graphics();
const leftForeArm = new Graphics();
const leftHand = new Graphics();
const rightUpperArm = new Graphics();
const rightForeArm = new Graphics();
const rightHand = new Graphics();
const leftThigh = new Graphics();
const leftCalf = new Graphics();
const leftFoot = new Graphics();
const rightThigh = new Graphics();
const rightCalf = new Graphics();
const rightFoot = new Graphics();
* μμ(upperArm): μ΄κΉ¨μμ νκΏμΉκΉμ§μ ν μΌλΆλΆμ μ§μΉν©λλ€. * μ μ(foreArm): νκΏμΉμμ μλͺ©κΉμ§μ ν μΌλΆλΆμ μ§μΉν©λλ€.
ν(λ€λ¦¬)μ μ μ λ° μμμ μμμ μ’νμ x,y λ³νλ(upperArmDxy
)μΌλ‘ 그릴 μ μλ Line
μΌλ‘ λνλμ΅λλ€.
// src/utils/drawLimb.js
const upperArmDxy = {
dx: limbLength * getCos(upperArmAngle) * flagX,
dy: -limbLength * getSin(upperArmAngle) * flagY,
};
upperArm.position.set(shoulder.x + flagX * flagY, shoulder.y);
upperArm
.lineStyle(limbWidth + 3, COLOR.SKIN)
.lineTo(upperArmDxy.dx, upperArmDxy.dy);
drawLimb.js
μ 체 μ½λ
μκ³Ό μ΄κΉ¨μ μ’νλ νμ μ μ μμ΅λλ€. λ°λΌμ μ κ·Έλ¦Όμμ theta1
κ³Ό theta2
λ₯Ό ꡬν μ μμ΅λλ€.
// src/utils/moveJoint.js
const handToShoulder = getDistance(shoulder, cursorInContainer);
const h = Math.sqrt(limbLength ** 2 - (handToShoulder / 2) ** 2) || 0; // μ΄λ±λ³μΌκ°ν HESμ λμ΄
const theta1 = getAngleDegrees(handToShoulder / 2, h);
const theta2 = getAngleDegrees(
flagX * (shoulder.x - cursorInContainer.x),
shoulder.y - cursorInContainer.y
);
νμ μ μκ³Ό μμμ κΈΈμ΄λ κ°κ² μ€μ νκ³ ,
μμμ΄ μ§λ©΄κ³Ό μ΄λ£¨λ κ° (theta1
- theta2
)λ₯Ό ꡬν΄μ νκΏμΉ(elbow
) μ’νλ₯Ό κ³μ°ν μ μμ΅λλ€.
const elbow = {
x: shoulder.x - flagX - limbLength * getCos(theta1 - theta2) * flagX,
y: shoulder.y + limbLength * getSin(theta1 - theta2),
};
νκΏμΉ(elbow
) μ’νλ₯Ό λ°νμΌλ‘ μμ(upperArm
)κ³Ό μ μ(foreArm
)μ 그립λλ€.
const upperArmDxy = {
x: elbow.x - shoulder.x,
y: elbow.y - shoulder.y,
};
upperArm
.lineStyle(limbWidth + 3, COLOR.SKIN)
.lineTo(upperArmDxy.x, upperArmDxy.y);
foreArm.position.set(elbow.x, elbow.y);
foreArm
.lineStyle(limbWidth, COLOR.SKIN)
.lineTo(hand.x - elbow.x, hand.y - elbow.y);
π½ μ μμΉμ λ°λΌ νμ΄ μμ°μ€λ½κ² μ νλ λͺ¨μ΅
ν΄λΌμ΄λ° λμμ μ¬λ¬ μ 체λΆμμ μμ§μμ΄ λ³΅ν©μ μΌλ‘ μ΄λ£¨μ΄μ§ κ²°κ³Όλ¬Όμ
λλ€.
(ex. μμ λ»μ΄ λ©λ¦¬ μλ 물체λ₯Ό μ‘λλ€ = μμ λλκ·Έν΄μ ν κ΄μ μ΄ μ΄λνλ€ + λͺΈν΅μ΄ μ λ°©ν₯μΌλ‘ μ΄λνλ©΄μ λ€λ₯Έ κ΄μ λ μμ§μΈλ€β)
μ²μμλ μμ μλ‘ λ»κ³ , λͺΈν΅ μ¬λ¦¬κ³ , μ λ»λ κ³Όμ μ ν΅ν΄μ 무κ²μ€μ¬μ μ¬λ¦¬κ² νμ΅λλ€.
κ·Έλ°λ° μ΄ μμ§μμ λΆμμ°μ€λ¬μ κ³ , μ¬μ©μ μ
μ₯μμ λΆνΈνμ΅λλ€.
κ·Έλμ ν μμ λ»λ λμμ ν λ, λ€μκ³Ό κ°μ λ¨κ³λ₯Ό κ±°μ³ μμ§μμ ꡬννμ΅λλ€.
-
λ¨Όμ ν κ΄μ μ μμ§μ΄λ ν¨μ
moveJoint()
λ₯Ό μ€νν©λλ€.
μ΄ κ³Όμ μμ μμμ μ΄κΉ¨κΉμ§ κ±°λ¦¬κ° μ νλ ν κΈΈμ΄λ₯Ό λμ΄μ λ€λ©΄theta2
λ₯Ό λ°ννκ³ , -
λͺΈν΅μ μμ§μ΄λ ν¨μ
moveBodyTo()
λ₯Ό μ€νν©λλ€.
const theta2 = moveJoint(
...leftArmList,
...armSize,
cursorInContainer,
1,
1,
handRadius
);
if (!theta2) return;
return moveBodyTo({
x: cursorInContainer.x + armLength * 2 * getCos(theta2) + BODY.WIDTH / 2,
y: cursorInContainer.y + armLength * 2 * getSin(theta2) + BODY.HEIGHT / 2,
});
moveBodyTo()
λ λͺΈν΅μ μμΉλ₯Ό λ°κΎΈκ³ ν¨μ λ΄λΆμμmoveJointBody()
ν¨μλ₯Ό μ€νν΄μ λ€λ₯Έ νλ€λ¦¬ κ΄μ μ λͺΈν΅μ μμΉμ λ°λΌ μμ°μ€λ½κ² μμ§μ΄κ² ν©λλ€.
library μμ΄ κ΅¬ννκ² λ λκΈ°
μ€λ ₯κ°μλμ μν μ 체λΆμ λ±κ°μ μμ΄λλ§ μ νννλ©΄ λκΈ° λλ¬Έμ 3rd party library μμ΄ μ§μ ꡬνν μ μκ² λ€λ μκ°μ΄ λ€μμ΅λλ€.
- λͺ¨λ μ¬λ¬Όμ 곡ν΅μ μΌλ‘ μμ©νλ μ€λ ₯ ν¨μλ₯Ό λ§λ λ€. β
- μ₯μ
- μ 체 λΆμ μΈμ λ€λ₯Έ κ°μ²΄μλ λ²μ©μ μΌλ‘ μ¬μ©ν μ μμ΅λλ€.
- μ μ§λ³΄μκ° μ©μ΄ν©λλ€.
- λ¬Έμ μ
- μ 체 λΆμλ λ€λ₯Έ λΆμμ μ°κ²°λμ΄μμΌλ―λ‘, λ¨μν μλλ°©ν₯μΌλ‘ μ€λ ₯μ΄ μμ©νλ κ² μ΄μΈμ μ₯λ ₯κ³Ό κ°μ λ€λ₯Έ νλ μμ©ν©λλ€.
- νμ ν©λ ₯μ ꡬν΄μ μ 체 μμ§μμ μ μ©νλ λ‘μ§μ ꡬννκΈ°μλ μκ°μ΄ λΆμ‘±νμ΅λλ€.
- μ₯μ
Pixi.JS
κΈ°λ° νλ¬κ·ΈμΈ ννλ‘ μΈμ²΄ μ€λ ₯ μμ© ν¨μλ₯Ό λ§λ λ€. β- μ€λ ₯μ νμ μμ©νκ³ μλλ°, νΉμ μν©μμ μ€λ ₯μ΄ λ€λ₯Έ νμ ν©λ ₯λ³΄λ€ ν¬κ² μμ©ν΄μ μ 체λΆμκ° μλ λ°©ν₯μΌλ‘ λ±κ°μμμ΄λνκ±°λ νλ μ΄μ΄κ° μλλ‘ μ΄λνλ€κ³ μ μ νκ³ λ‘μ§μ ꡬμ±νμ΅λλ€.
ν(λ€λ¦¬)λ μ΄κΉ¨(κ³ κ΄μ )μ νμ μΆμΌλ‘ ν΄μ μ€λ ₯ μμ© λ°©ν₯μΌλ‘ λ±κ°μ μμ΄λμ ν©λλ€.
-
μμ΄ νλμμ λ¨μ΄μ‘λ€λ κ²μ μ΄λ»κ² μ μ μμκΉ?
νλ κ°κ°μ μ μ₯μμpointerdown
μ΄λ²€νΈ λ°μ μ΄νcanvas
μμ pointerup μ΄λ²€νΈκ° λ°μνλ©΄ μ€λ ₯ μμ© ν¨μλ₯Ό μ€ννλ€.
β νμ νλ μμ μλ°μ΄ μκΈ° λλ¬Έμ, νλλpointerdown
μ΄λ²€νΈλ₯Ό κ°μ§ν μ μμ΅λλ€.- νλ μμΉ κΈ°λ°μΌλ‘ κ°μ§νλ€.
μ/λ°μμpointerup
μ΄λ²€νΈκ° λ°μν λ, μ/λ°μ΄ μ ν΄μ§ νλ μ’ν λ΄μ μμΉνμ§ μμΌλ©΄ μ€λ ₯ μμ© ν¨μλ₯Ό μ€νν©λλ€. (ν/λ€λ¦¬λ μ΄κΉ¨/κ³ κ΄μ μ’νλ₯Ό νμ μΆμΌλ‘ λ±κ°μ μμ΄λμ νλ€.)- μ΄ λ, νλ μ΄μ΄μ 체λ ₯(HP) μλͺ¨κ° ν° κ²μΌλ‘ κ°μ£Όνμ¬ HPκ° λΉ¨λ¦¬ μ€μ΄λλλ€.
-
λ±κ°μ μμ΄λμ μ΄λ»κ² ννν κΉ?
gravityRotate()
ν¨μλ₯Ό μ€νν΄μ μμ λ° μ μμ νμ κ°μλ(angleVelocity
)λ₯Ό μΌμ ν κ°μλλ‘ μ¦κ°μν΅λλ€.- μ€λ ₯μ νμ μλλ°©ν₯μΌλ‘ μμ©νκΈ°μ, μ§λ©΄μ μμ§μΈ μ κ³Ό νμ΄ μ΄λ£¨λ κ°λμ κ°μμ§ λκΉμ§
μμμ κ°λ(upperArm.angle
) λ° μ μμ κ°λ(foreArm.angle
)λ₯Ό μ¦κ°μν΅λλ€.
// src/utils/gravityRotate.js const handToShoulder = getDistance(shoulder, hand); const h = Math.sqrt(limbLength ** 2 - (handToShoulder / 2) ** 2) || 0; const theta1 = getAngleDegrees(handToShoulder / 2, h); const upperArmOriginalAngle = getAngleDegrees( foreArm.y - shoulder.y, foreArm.x - shoulder.x ); const rotatingDirection = upperArmOriginalAngle / Math.abs(upperArmOriginalAngle); let angleVelocity = 0; function rotateArm() { angleVelocity += 0.5; const isUpperArmRotating = Math.abs(upperArm.angle) < Math.abs(upperArmOriginalAngle); const foreArmRotatingGoal = Math.abs(upperArmOriginalAngle) + theta1 * 2 * rotatingDirection * flagX; const isForeArmRotating = Math.abs(foreArm.angle) < foreArmRotatingGoal; if (isUpperArmRotating) { upperArm.angle += angleVelocity * 0.2 * rotatingDirection; const newAngle = upperArmOriginalAngle - upperArm.angle; foreArm.x = shoulder.x + limbLength * getSin(newAngle); foreArm.y = shoulder.y + limbLength * getCos(newAngle); } if (isForeArmRotating) { foreArm.angle += angleVelocity * 0.2 * rotatingDirection; } const newAngle = foreArmRotatingGoal - Math.abs(foreArm.angle); hand.x = foreArm.x + limbLength * getSin(newAngle) * rotatingDirection; hand.y = foreArm.y + limbLength * getCos(newAngle); const isRotationFinished = !isUpperArmRotating && !isForeArmRotating; if (isRotationFinished) { return drawLimb( ... ); // νμ μ΄ λλλ©΄ κ°λκ° 0μΌλ‘ 리μ λ μλ‘μ΄ νλ€λ¦¬λ₯Ό 그립λλ€. } requestAnimationFrame(rotateArm); } rotateArm();
λͺΈ μ μ²΄κ° μλ λ°©ν₯μΌλ‘ λ±κ°μ μ΄λμ ν©λλ€.
- νλ μ’ν λ²μ μμ μλ μΌμ/μ€λ₯Έμμ κ°μλ₯Ό λ³μλ‘ μ μ₯ν©λλ€.
- κ°κ° μ΄κΈ°κ°μ 1κ°μ΄λ©°,
onDragEnd()
ν¨μλ₯Ό μ€νν λλ§λ€ μμ΄ νλ μμΉμ μλμ§ νλ³ν΄μ λ³μκ°μ λ³κ²½ν©λλ€.
- κ°κ° μ΄κΈ°κ°μ 1κ°μ΄λ©°,
onDragStart()
ν¨μ μ€ν μ μμ κ°μκ° λͺκ°μΈμ§ νμΈνκ³ , 0κ°λΌλ©΄fallDown()
ν¨μλ₯Ό μ€νν©λλ€.fallDown()
ν¨μλ₯Ό μ€ννλ©΄ νλ μ΄μ΄κ° ν¬ν¨λplayerContainer
κ° λ°λ₯μ λΏμ λκΉμ§
νκ° μλ(descentVelocity
)λ₯Ό μΌμ ν κ°μλλ‘ μ¦κ°μν€λ©΄μ μλλ‘ μ΄λν©λλ€.function fallDown(displayText) { let descentVelocity = 0; function descend() { descentVelocity += 0.4; playerContainer.y += descentVelocity * 0.3; const isPlayerAboveGround = playerContainer.y < containerPosition.y - leftShoulder.y + (initialContainerHeight - playerContainer.height); if (!isPlayerAboveGround) { gameStatus.fail = true; holdContainer.addChild(getResultText(displayText)); return; } requestAnimationFrame(descend); } descend(); }
νμͺ½ νμ΄ ν΄μ§ λκΉμ§ 무κ²μ€μ¬μ΄ μ€λ ₯μ λ°μμ μλλ‘ λ΄λ €κ°λλ€.
- λλκ·Έκ° λλκ³
onDragEnd()
ν¨μκ° μ€νλλ©΄,checkGravityCenter()
ν¨μλ₯Ό μ€νν΄μ 무κ²μ€μ¬ xμ’νκ° μ λ°μ xμ’ν μ¬μ΄μ μλμ§ νλ³ν©λλ€. - 무κ²μ€μ¬ xμ’νκ° μ λ° μ¬μ΄μ μμΌλ©΄
descendByGravity()
ν¨μλ₯Ό μ€νν΄μ λͺΈν΅μ΄ μλλ‘ λ΄λ €κ°λλ€.
function checkGravityCenter() {
const gravityCenterX = body.x + BODY.WIDTH / 2;
attachedStatus.isStable =
leftFoot.x < gravityCenterX && gravityCenterX < rightFoot.x;
if (!attachedStatus.isStable) {
descendByGravity();
}
function descendByGravity() { ... }
}
- μ΄ λ, νλ μ΄μ΄μ 체λ ₯(HP) μλͺ¨κ° ν° κ²μΌλ‘ κ°μ£Όνμ¬ HPκ° λΉ¨λ¦¬ μ€μ΄λλλ€.
* hand
: μ/λ°μ μ§μΉν©λλ€.
hand
κ°μ²΄μpointermove
μ΄λ²€νΈλ₯Ό λ±λ‘ν΄μ 컀μλ‘ μμ λλκ·Έν λonDragging()
ν¨μλ₯Ό μ€νν©λλ€.onDragging()
ν¨μμμmoveJoint()
ν¨μλ₯Ό μ€ννκ³ ,
moveJoint()
ν¨μμμhand
x,y μ’νμ 컀μμ x,y μ’νλ₯Ό λμ ν¨μΌλ‘μ¨hand
λ₯Ό μ΄λμν΅λλ€.
- μ μ΄λ μλκ° λ§μ°μ€ λλκ·Έ μλλ₯Ό λͺ» λ°λΌκ°μ λλκ·Έ μ€μ λμμ΄ λκΈ°λ κ²½μ°κ° μμκ³ μ¬μ©μ κ²½νμ΄ μ νλμμ΅λλ€.
- 컀μ μ΄λ μλκ° λΉ λ₯Ό λ, λλκ·Έ μ€μ μ€μκ°μΌλ‘
hand
x,y μ’νκ° μ»€μμ x,y μ’νλ‘ μ λ°μ΄νΈλμ§ μμμ΅λλ€. - λ°λΌμ, 컀μμ μ’νκ°
hand
μ’νλ₯Ό λ²μ΄λλ νμμ΄ λΉλ²νκ² λ°μνμ΅λλ€.
addEventListener("pointermove", onDragging)
μ΄λ²€νΈλ₯Όhand
κ°μ²΄κ° μλλΌ λ·λ°°κ²½μΈ λ²½μ λνλ΄λdocument.querySelector(".wall")
μ λ±λ‘ν©λλ€.const wall = document.querySelector(".wall"); wall.addEventListener("pointermove", onDragging);
- 컀μ μμΉκ°
hand
μ’ν λ²μλ₯Ό λ²μ΄λλλΌλ (=μ΄κΉ¨μμ 컀μκΉμ§μ κ±°λ¦¬κ° ν κΈΈμ΄(limbLength * 2
)λ³΄λ€ κΈΈμ΄μ§λλΌλ)
μ΄κΉ¨βμ 벑ν°
κ°μ΄κΉ¨β컀μ 벑ν°
μ λ°©ν₯λ§ κ°κ³ ν¬κΈ°λ ν κΈΈμ΄λ‘ μΌμ νλλ‘hand
μ’νλ₯Ό μ λ°μ΄νΈν©λλ€.// src/utils/moveJoint.js const cursorToShoulder = getDistance(shoulder, cursorInContainer); ... if (cursorToShoulder > limbLength * 2) { hand.x = shoulder.x - limbLength * 2 * getCos(theta2) * flagX; hand.y = shoulder.y - limbLength * 2 * getSin(theta2); } else { hand.x = cursorInContainer.x; hand.y = cursorInContainer.y; }
- μ¬μ©μκ° μ»€μλ₯Ό λΉ λ₯΄κ² μμ§μ¬μ 컀μ μμΉκ° μμ μ‘°κΈ λ²μ΄λλ μμ΄ μ»€μ μͺ½μΌλ‘ μ΄λν©λλ€.
- ν κΈΈμ΄λ³΄λ€ λ©λ¦¬ μμ λλκ·Έν΄λ μμ νμ λΆμ΄μλ, 컀μ λ°©ν₯μΌλ‘ μμ§μ λλ€.
νλ€λ¦¬κ° ν λ² ν΄μ§ νμ λ€λ₯Έ μͺ½ μ/λ°μ μμ§μ¬μ ν΄μ§ λΆλΆμ κ΅½ν μ μμκΉ?
- (λλκ·Έλ₯Ό νκ±°λ μ€λ ₯μ μν΄ ν/λ€λ¦¬κ° μλλ°©ν₯μΌλ‘ λ¨μ΄μ Έμ) νμͺ½ ν/λ€λ¦¬κ° ν΄μ§ ν, λ€λ₯Έ μ/λ°μ λλκ·Ένμ λ ν λ² ν΄μ§ νλ€λ¦¬κ° κ΅½νμ§μ§ μμμ΅λλ€.
- ν΄μ§ μͺ½μ μ/λ°μ μ§μ λλκ·Έν΄μ κ΅½νλ λ°©λ²λ μμμΌλ, μ΄λ μ¬μ©μ μ μ₯μμ λΆνΈνμ΅λλ€.
- νμͺ½ ν/λ€λ¦¬κ° ν΄μ§ ν λ€λ₯Έ μ/λ°μ λλκ·Ένμ λ λͺΈν΅μ μμ§μ΄λ ν¨μκ° λμνμ§ μμμ΅λλ€.
- λλκ·Έλ₯Ό λ©λ¦¬ ν΄λ μ 체λΆμκ° λͺΈμμ λ¨μ΄μ Έλκ°μ§ μλλ‘ νκΈ° μν΄, λͺΈν΅ μμ§μ κ΄λ ¨ ν¨μλ νμͺ½ νλ€λ¦¬κ° ν΄μ§λ©΄ λμμ νμ§ μλλ‘ μ€κ³λμ΄μμμ΅λλ€.
-
μ 체λΆμλ₯Ό μ¬μ λ ¬μν€λ ν¨μ
rearragneBody()
λ₯Ό μμ±ν©λλ€. -
pointerup
μ΄λ²€νΈκ° λ°μνμ λλ μ€λ ₯μ μν΄ ν/λ€λ¦¬κ° μΌμ§μ μΌλ‘ ν΄μ§κ³ λ νμ μ΄ ν¨μλ₯Ό νΈμΆν©λλ€. -
rearragneBody()
μμ μ΄κΉ¨/κ³ κ΄μ κΈ°μ€μΌλ‘ μ/λ°μ΄ μμΉν λ°©ν₯μflag
λ³μμ ν λΉνκ³ ,moveBodyTo()
ν¨μλ₯Ό μ€νμμΌ μ 체λΆμλ€μ μ¬μ λ ¬ν©λλ€.function rearrangeBody(part) { if (!attachedStatus.leftHand && dragTarget !== leftHand) { leftHand.position.set(leftShoulder.x, leftShoulder.y + armLength * 2 - 2); } else if (!attachedStatus.rightHand && dragTarget !== rightHand) { rightHand.position.set( rightShoulder.x, rightShoulder.y + armLength * 2 - 2 ); } else if (!attachedStatus.leftFoot && dragTarget !== leftFoot) { leftFoot.position.set(leftCoxa.x, leftCoxa.y + legLength * 2 - 2); } else if (!attachedStatus.rightFoot && dragTarget !== rightFoot) { rightFoot.position.set(rightCoxa.x, rightCoxa.y + legLength * 2 - 2); } if (!part) return; const flag = { x: null, y: null }; flag.x = part.hand.x < part.shoulder.x ? -1 : 1; flag.y = part.hand.y < part.shoulder.y ? -1 : 1; exceededPart = null; const rearrangePX = 3; moveBodyTo({ x: body.x + rearrangePX * flag.x + BODY.WIDTH / 2, y: body.y + rearrangePX * flag.y + BODY.HEIGHT / 2, }); }
- ν/λ€λ¦¬κ° ν΄μ§ ν
pointerup
μ΄λ²€νΈκ° λ°μν λ,
rearragneBody
ν¨μλ₯Ό μ€νν΄μ ν΄μ§ λΆλΆμ μ‘°κΈ κ΅½νμΌλ‘μ¨ λ€μ λλκ·Έ λμμ λνλΌ μ μκ² λμμ΅λλ€.
μ΄ κ²μμμλ μ€λ ₯μ μν μ λλ©μ΄μ
ν¨κ³Όκ° μμ΅λλ€. (ν/λ€λ¦¬κ° μλλ‘ λ¨μ΄μ§, λ μμ λμμ λ μλλ‘ μΆλ½ν¨)
μ΄λ₯Ό λΆλλ½κ² λνλ΄κΈ° μν΄μ μ²μμλ setInterval()
μ μ¬μ©νμΌλ λͺκ°μ§ μ°¨μ΄μ λλ¬Έμ requestAnimationFrame()
μΌλ‘ μμ νμ΅λλ€.
setInterval()
μ μΈμλ₯Ό λ겨 μ΄λΉ νΈμΆ νμλ₯Ό μ§μ ν μ μμ΅λλ€.rAF()
λ λΈλΌμ°μ μ 리μμ€ & μ»΄ν¨ν°μ CPU μ±λ₯μ κ³ λ €νμ¬ μ΄λΉ μ€ννμκ° κ²°μ λ©λλ€.(κΈ°λ³Έ 60FPS)
setInterval()
λ‘ μ λλ©μ΄μ μ λ§λ€ λλ, funcκ³Ό delayλ§ μ€μ ν΄μ£Όλ©΄ λ©λλ€.setInterval(func, delay);
rAF()
μ κ²½μ°,rAF()
μ callbackλ΄λΆμμrAF()
λ₯Ό μ¬νΈμΆν΄μ€μΌ μ λλ©μ΄μ μ€νμ΄ κ°λ₯ν©λλ€.requestAnimationFrame(render); function render() { ... requestAnimationFrame(render); }
setInterval()
μ κ³ μ ν idκ°μ 리ν΄νλ―λ‘,clearInterval()
μ ν΄λΉ idκ°μ λ겨주면 μ€λ¨ κ°λ₯ν©λλ€.rAF()
λ κ³ μ ν idκ°μ 리ν΄νλλ°, μ΄ idκ°μcancelAnimationFrame()
μ λ겨주면 μ€λ¨ κ°λ₯ν©λλ€.
setInterval()
μΌλ‘ ꡬνν μ λλ©μ΄μ μ μ½κ°μ νλ μ λκΉμ΄ λ°μνκ±°λ νλ μ μ체λ₯Ό λΉ λ¨λ¦¬λ λ¬Έμ κ° λ°μν μ μμ΅λλ€.rAF()
μ μ λλ©μ΄μ μ μν΄ μ΅μ νλ ν¨μμ΄λ―λ‘ μ λλ©μ΄μ μ΄ μ€νλλ νκ²½μ κ΄κ³ μμ΄ μ μ ν νλ μ μλλ‘ μ€νλλ©°, νμ΄ νμ±νλμ§ μμ μνμ΄κ±°λ μ λλ©μ΄μ μ΄ νμ΄μ§λ₯Ό λ²μ΄λ κ²½μ°μλ κ³μ μ€νλλ κΈ°μ‘΄μ λ¬Έμ μ μ ν΄κ²°ν μ μμ΅λλ€.
λΈλΌμ°μ μμ μ¬λ¬ νμ λμλκ³ μμ λ νμ¬ μΉνμ΄μ§κ° λΉνμ±νλμ΄μμΌλ©΄
setInterval()
μ λ°±κ·ΈλΌμ΄λμμ νΈμΆλλ μκ°λ§λ€ κ³μ μ€νλμ§λ§rAF()
μ νλ©΄μ repaintκ° μΌμ΄λ λ νΈμΆλλ―λ‘ λ°±κ·ΈλΌμ΄λμμ νΈμΆλμ§ μκ³ λκΈ°ν©λλ€.
- 리λ λλ§μ΄ λλμ§ μμλλ° μ λλ©μ΄μ
μ μννλ λͺ
λ Ήμ΄ λ΄λ €μ§λ€λ©΄ μνλλλ‘ μ λλ©μ΄μ
μ΄ λΆλλ½κ² μ§νλμ§ μμ΅λλ€.
리νμΈνΈκ° λλ ν μ μ©ν μ λλ©μ΄μ μrequestAnimationFrame()
μ μ½λ°±μΌλ‘ λ£μ΄μ£Όλ©΄ μμ°μ€λ¬μ΄ μ λλ©μ΄μ μ΄ μμ±λ©λλ€. setInterval()
κ³Ό λ¬λ¦¬ νλ μ μμ± μ΄κΈ° λ¨κ³μ λ§μΆ° μ λλ©μ΄μ μ΄ νΈμΆλμ΄μ λ λΆλλ¬μ΄ λμμ΄ κ°λ₯ν©λλ€.
- React
- React router
- Redux-toolkit
- Styled Components
- Pixi.js
- ESLint
- Jest
- Node.js
- Express
- MongoDB Atlas / Mongoose
- ESLint
- μ±λ₯
- WebGL 2D λ λλ§μ κ΄λ ¨λ κΈ°λ₯λ€λ§ λ€μ΄μκΈ° λλ¬Έμ κ΅μ₯ν λΉ λ₯΄κ³ κ°λ³μ΅λλ€.
- ν¬λ‘μ€ νλ«νΌ νΈνμ±
- λ€μν νλ«νΌκ³Ό κΈ°κΈ°μμ μννκ² μλνλλ‘ μ€κ³λμμ΅λλ€.
- μ¬μ©μ νΈμμ±
- μ§κ΄μ μ΄κ³ κ°λ¨ν APIλ₯Ό μ 곡ν©λλ€.
- 곡μλ¬Έμμ μ λ¦¬κ° μ λμ΄μκ³ μμ κ° νλΆν©λλ€.
- μ€ν€λ§ μ μ°μ±
- SQLμ λΉν΄ μ€ν€λ§κ° μ μ°νλ©°, λ€μν λ°μ΄ν° μ νκ³Ό ꡬ쑰λ₯Ό μ²λ¦¬ν μ μμ΅λλ€.
- μ ν΄μ§ κ΅¬μ‘°κ° μμΌλ―λ‘ λ°μ΄ν° κ΅¬μ‘°κ° μμ£Ό μΆκ°, μμ , λ³κ²½λλ κ²½μ° μ μ°νκ² μ μ©μν¬ μ μμ΅λλ€.
- μ§κ΄μ μΈ λ°μ΄ν° λͺ¨λΈ
- λ°μ΄ν°λ₯Ό ν(row) λμ λνλ¨ΌνΈ(document)μ μ μ₯νκ³ , μ΄λ
JSON
μ κΈ°λ°ν©λλ€. λ°λΌμ μ¬λ¬ ν μ΄λΈ κ°μ 볡μ‘ν μ‘°μΈ μ°μ° μμ΄ λ°μ΄ν°μ κ³μΈ΅ ꡬ쑰λ₯Ό μ½κ² νμ ν μ μμ΅λλ€.
- λ°μ΄ν°λ₯Ό ν(row) λμ λνλ¨ΌνΈ(document)μ μ μ₯νκ³ , μ΄λ
- Scale out ꡬ쑰
- μνμ νμ₯μ΄ κ°λ₯νλ©°, λ°μ΄ν°λ² μ΄μ€λ₯Ό μ¬λ¬ λμ μλ²μ λΆμ°μν΄μΌλ‘μ¨ μ©λμ λ릴 μ μμ΅λλ€.
*νλ: μλ°λ‘ μ‘μ μ μλ λ²½μ λΆμ΄μλ λ ννλ₯Ό μλ―Έν©λλ€.
- νλμ λ°°κ²½μ μλ λͺ¨λ 물체λ€μ μ‘κ±°λ λ°μ μ μλ νλμ λλ€.
- μ¬μ©μλ νλ μ΄μ΄μ μ/λ°/λͺΈν΅μ λλκ·Έν΄μ μμ§μΌ μ μμ΅λλ€.
- νλ μ΄μ΄μ μ/λ°μ λλκ·Έν΄μ νλ μμ λμΌλ©΄ κ³ μ λ©λλ€. κ·Έλ μ§ μμΌλ©΄ μ/λ°μ΄ μλλ‘ λ¨μ΄μ§λλ€.
- 무κ²μ€μ¬μ xμ’νκ° μ λ° μ¬μ΄μ μμΌλ©΄ νμͺ½ νμ΄ ν΄μ§ λκΉμ§ 무κ²μ€μ¬μ΄ μ€λ ₯μ λ°μμ μλλ‘ λ΄λ €κ°λλ€.
- μ/λ°μ΄ νλμμ λ¨μ΄μ§κ±°λ 무κ²μ€μ¬μ΄ μ λ° μ¬μ΄μ μμΌλ©΄, 체λ ₯(HP) μλͺ¨κ° ν° κ²μΌλ‘ κ°μ£Όνμ¬ HPκ° λΉ¨λ¦¬ μ€μ΄λλλ€.
- μμμΌλ‘ TOPνλλ₯Ό μ‘μΌλ©΄ μλ±(μ±κ³΅)μ΄λ©°, λνΉμ 보μ κΈ°λ‘μ΄ λ±λ‘λ©λλ€.
- λνΉμ λ±λ° μκ°μ΄ 짧μ μμλλ‘ λμμ§λ©°, κ°μ μκ°μΌ κ²½μ° HPκ° λ§μ΄ λ¨μ μ¬λμ΄ μμκ° λμμ§λλ€.
- 1 μ£Όμ°¨: κΈ°ν λ° μ€κ³
- 2~3 μ£Όμ°¨: κΈ°λ₯ κ°λ°
- 4 μ£Όμ°¨: ν μ€νΈμ½λ μμ±, λ°ν
μΈλ€μΌμ ν΄λ¦νλ©΄ κ²μ μμ° μμ μ νλΈ λ§ν¬λ‘ μ΄λν©λλ€.
π½ 첫λ²μ§Έ 루νΈ
Canvas API
λ‘ κ²μμ λ§λ κ²μ μ΄λ²μ΄ μ²μμ΄μμ΅λλ€. κ·Έλ¦¬κ³ κΈ°λ₯ κ°λ°μ ν λ μ°Έκ³ ν λ§ν λΉμ·ν νλ‘μ νΈκ° μμκ³ , κ΄μ μμ§μ λ° λ¬Όλ¦¬μμ§ κ΄λ ¨ λΌμ΄λΈλ¬λ¦¬ μμ΄ μμνκ² μ λ
Όλ¦¬λ‘ κΈ°λ₯μ ꡬννλ κ²μ μ½μ§ μμμ΅λλ€. κ·ΈλΌμλ λΆκ΅¬νκ³ , ν¨μμ μ¬μ¬μ©μ±μ κ³ λ €νλ©° μ°¨κ·Όμ°¨κ·Ό λ‘μ§μ ꡬμ±νκ³ μμν λ¬Έμ λ₯Ό ν΄κ²°νλ κ³Όμ μ λΏλ―ν κ²½νμ΄μμ΅λλ€.
μ λ ν΄λΌμ΄λ°μ μ λ§ μ’μν©λλ€. νΉν μλ± λͺ» ν κ² κ°μ 루νΈλ₯Ό μ±κ³΅νμ λ μ±μ·¨κ°μ μ μΆμ μλλ ₯μ μΌλΆκ° λκΈ°λ ν©λλ€.
μ΄ κ²μμ ν΅ν΄ λ€λ₯Έ μ¬λλ€λ λΆκ°λ₯ν΄λ³΄μ΄λ λͺ©νλ₯Ό μ±μ·¨νλ μ¦κ±°μμ λλΌλ©΄ μ’κ² μ΅λλ€.