여러분이 사용하고 계신 브라우저는 HTML5를 지원하지 않기 때문에 몇몇 요소가 제대로 보이도록 JScript를 사용하고 있습니다. 하지만 여러분의 브라우저 설정에서 스크립트 기능이 꺼져있으므로, 현재 페이지를 제대로 확인하시려면 스크립트 기능을 켜주셔야 합니다. HTML 게임 - 바운싱

HTML 게임 – 바운싱

1개월전 작성

HTML Canvas 게임 바운싱

HTML Canvas 게임에서 바운싱 효과를 구현하는 방법을 알아본다. 오브젝트가 벽이나 경계에 부딪혔을 때 자연스럽게 튀어오르는 물리적 효과를 만드는 다양한 기법을 설명한다.

기본 벽면 바운싱

게임 오브젝트가 캔버스 경계에서 튀어오르는 기본적인 바운싱을 구현해보자.

<canvas id="myCanvas1" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas1"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 2;
this.speedY = 1;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
this.checkBounds();
}
this.checkBounds = function() {
if (this.x <= 0 || this.x >= myGameArea.canvas.width - this.width) {
this.speedX = -this.speedX;
}
if (this.y <= 0 || this.y >= myGameArea.canvas.height - this.height) {
this.speedY = -this.speedY;
}
}
}
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>

checkBounds() 메서드에서 오브젝트가 캔버스 경계에 닿으면 해당 방향의 속도를 반전시켜 바운싱 효과를 만든다.

원형 오브젝트 바운싱

원형 오브젝트의 바운싱을 구현해보자.

<canvas id="myCanvas2" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas2"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(radius, color, x, y) {
this.radius = radius;
this.speedX = 3;
this.speedY = 2;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
this.checkBounds();
}
this.checkBounds = function() {
if (this.x - this.radius <= 0 || this.x + this.radius >= myGameArea.canvas.width) {
this.speedX = -this.speedX;
this.x = Math.max(this.radius, Math.min(this.x, myGameArea.canvas.width - this.radius));
}
if (this.y - this.radius <= 0 || this.y + this.radius >= myGameArea.canvas.height) {
this.speedY = -this.speedY;
this.y = Math.max(this.radius, Math.min(this.y, myGameArea.canvas.height - this.radius));
}
}
}
function startGame() {
myGamePiece = new component(20, "blue", 50, 100);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>

원형 오브젝트는 중심점에서 반지름만큼 떨어진 거리를 경계로 계산한다. 경계를 벗어나지 않도록 위치를 조정하는 것도 중요하다.

탄성 계수가 있는 바운싱

탄성 계수를 적용하여 바운싱할 때마다 속도가 감소하는 효과를 추가해보자.

<canvas id="myCanvas3" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myGamePiece;
var myGameArea = {
canvas: document.getElementById("myCanvas3"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(radius, color, x, y) {
this.radius = radius;
this.speedX = 4;
this.speedY = 3;
this.x = x;
this.y = y;
this.bounce = 0.8;
this.friction = 0.99;
this.gravity = 0.1;
this.gravitySpeed = 0;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.speedX *= this.friction;
this.speedY *= this.friction;
this.gravitySpeed *= this.friction;
this.x += this.speedX;
this.y += this.speedY + this.gravitySpeed;
this.checkBounds();
}
this.checkBounds = function() {
if (this.x - this.radius <= 0) { this.x = this.radius; this.speedX = -this.speedX * this.bounce; } if (this.x + this.radius >= myGameArea.canvas.width) {
this.x = myGameArea.canvas.width - this.radius;
this.speedX = -this.speedX * this.bounce;
}
if (this.y - this.radius <= 0) { this.y = this.radius; this.speedY = -this.speedY * this.bounce; this.gravitySpeed = -this.gravitySpeed * this.bounce; } if (this.y + this.radius >= myGameArea.canvas.height) {
this.y = myGameArea.canvas.height - this.radius;
this.speedY = -this.speedY * this.bounce;
this.gravitySpeed = -this.gravitySpeed * this.bounce;
}
}
}
function startGame() {
myGamePiece = new component(15, "green", 100, 50);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.newPos();
myGamePiece.update();
}
startGame();
</script>

bounce 속성은 탄성 계수를, friction은 마찰력을, gravity는 중력을 나타낸다. 시간이 지날수록 공이 천천히 멈추는 현실적인 물리 효과를 구현했다.

다중 공 바운싱

여러 개의 공이 동시에 바운싱하는 시스템을 만들어보자.

<canvas id="myCanvas4" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myBalls = [];
var myGameArea = {
canvas: document.getElementById("myCanvas4"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(radius, color, x, y) {
this.radius = radius;
this.speedX = (Math.random() - 0.5) * 6;
this.speedY = (Math.random() - 0.5) * 6;
this.x = x;
this.y = y;
this.bounce = 0.9;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
ctx.stroke();
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
this.checkBounds();
}
this.checkBounds = function() {
if (this.x - this.radius <= 0) { this.x = this.radius; this.speedX = -this.speedX * this.bounce; } if (this.x + this.radius >= myGameArea.canvas.width) {
this.x = myGameArea.canvas.width - this.radius;
this.speedX = -this.speedX * this.bounce;
}
if (this.y - this.radius <= 0) { this.y = this.radius; this.speedY = -this.speedY * this.bounce; } if (this.y + this.radius >= myGameArea.canvas.height) {
this.y = myGameArea.canvas.height - this.radius;
this.speedY = -this.speedY * this.bounce;
}
}
}
function getRandomColor() {
var colors = ["red", "blue", "green", "purple", "orange", "pink", "yellow", "cyan"];
return colors[Math.floor(Math.random() * colors.length)];
}
function startGame() {
for (var i = 0; i < 8; i++) { var radius = Math.random() * 15 + 10; var x = Math.random() * (myGameArea.canvas.width - 2 * radius) + radius; var y = Math.random() * (myGameArea.canvas.height - 2 * radius) + radius; var color = getRandomColor(); myBalls.push(new component(radius, color, x, y)); } myGameArea.start(); } function updateGameArea() { myGameArea.clear(); for (var i = 0; i < myBalls.length; i++) { myBalls[i].newPos(); myBalls[i].update(); } } startGame(); </script>[/code]

무작위 크기, 색상, 초기 속도를 가진 여러 개의 공이 각각 독립적으로 바운싱한다.

공끼리 충돌

공들이 서로 충돌했을 때 바운싱하는 효과를 추가해보자.

[code lang="markup"]<canvas id="myCanvas5" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myBalls = [];
var myGameArea = {
canvas: document.getElementById("myCanvas5"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(radius, color, x, y) {
this.radius = radius;
this.speedX = (Math.random() - 0.5) * 4;
this.speedY = (Math.random() - 0.5) * 4;
this.x = x;
this.y = y;
this.mass = radius;
this.bounce = 0.8;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.stroke();
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
this.checkWallBounds();
}
this.checkWallBounds = function() {
if (this.x - this.radius <= 0) { this.x = this.radius; this.speedX = -this.speedX * this.bounce; } if (this.x + this.radius >= myGameArea.canvas.width) {
this.x = myGameArea.canvas.width - this.radius;
this.speedX = -this.speedX * this.bounce;
}
if (this.y - this.radius <= 0) { this.y = this.radius; this.speedY = -this.speedY * this.bounce; } if (this.y + this.radius >= myGameArea.canvas.height) {
this.y = myGameArea.canvas.height - this.radius;
this.speedY = -this.speedY * this.bounce;
}
}
this.checkCollision = function(other) {
var dx = this.x - other.x;
var dy = this.y - other.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.radius + other.radius) { var angle = Math.atan2(dy, dx); var sin = Math.sin(angle); var cos = Math.cos(angle); var vx1 = this.speedX * cos + this.speedY * sin; var vy1 = this.speedY * cos - this.speedX * sin; var vx2 = other.speedX * cos + other.speedY * sin; var vy2 = other.speedY * cos - other.speedX * sin; var finalVx1 = ((this.mass - other.mass) * vx1 + 2 * other.mass * vx2) / (this.mass + other.mass); var finalVx2 = ((other.mass - this.mass) * vx2 + 2 * this.mass * vx1) / (this.mass + other.mass); this.speedX = finalVx1 * cos - vy1 * sin; this.speedY = vy1 * cos + finalVx1 * sin; other.speedX = finalVx2 * cos - vy2 * sin; other.speedY = vy2 * cos + finalVx2 * sin; var overlap = this.radius + other.radius - distance; var separateX = overlap * cos * 0.5; var separateY = overlap * sin * 0.5; this.x += separateX; this.y += separateY; other.x -= separateX; other.y -= separateY; } } } function startGame() { var colors = ["red", "blue", "green", "purple", "orange"]; for (var i = 0; i < 5; i++) { var radius = Math.random() * 20 + 15; var x = Math.random() * (myGameArea.canvas.width - 2 * radius) + radius; var y = Math.random() * (myGameArea.canvas.height - 2 * radius) + radius; myBalls.push(new component(radius, colors[i], x, y)); } myGameArea.start(); } function updateGameArea() { myGameArea.clear(); for (var i = 0; i < myBalls.length; i++) { for (var j = i + 1; j < myBalls.length; j++) { myBalls[i].checkCollision(myBalls[j]); } myBalls[i].newPos(); myBalls[i].update(); } } startGame(); </script>[/code]

두 공이 충돌했을 때 운동량 보존 법칙을 적용하여 현실적인 충돌 효과를 구현했다. 질량과 속도를 고려한 탄성 충돌 공식을 사용한다.

패들과 공 바운싱

플레이어가 조작하는 패들과 공이 상호작용하는 바운싱 게임을 만들어보자.

[code lang="markup"]<canvas id="myCanvas6" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myBall;
var myPaddle;
var myGameArea = {
canvas: document.getElementById("myCanvas6"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
if (type == "ball") {
this.radius = width / 2;
this.speedX = 3;
this.speedY = -2;
}
this.update = function() {
ctx = myGameArea.context;
if (this.type == "ball") {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x + this.radius, this.y + this.radius, this.radius, 0, 2 * Math.PI);
ctx.fill();
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.checkBounds = function() {
if (this.type == "ball") {
if (this.x <= 0 || this.x >= myGameArea.canvas.width - this.width) {
this.speedX = -this.speedX;
}
if (this.y <= 0) { this.speedY = -this.speedY; } if (this.y >= myGameArea.canvas.height - this.height) {
this.x = myGameArea.canvas.width / 2 - this.radius;
this.y = myGameArea.canvas.height / 2 - this.radius;
this.speedX = 3;
this.speedY = -2;
}
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
this.bounceOff = function(otherobj) {
if (this.type == "ball") {
var ballCenterX = this.x + this.radius;
var paddleCenterX = otherobj.x + otherobj.width / 2;
var hitPos = (ballCenterX - paddleCenterX) / (otherobj.width / 2);
this.speedX = hitPos * 3;
this.speedY = -Math.abs(this.speedY);
}
}
}
function startGame() {
myBall = new component(20, 20, "red", 240, 135, "ball");
myPaddle = new component(80, 10, "blue", 200, 250);
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
myPaddle.speedX = 0;
if (myGameArea.key && myGameArea.key == 37) {myPaddle.speedX = -3; }
if (myGameArea.key && myGameArea.key == 39) {myPaddle.speedX = 3; }
myPaddle.newPos();
myBall.newPos();
myBall.checkBounds();
if (myBall.crashWith(myPaddle)) {
myBall.bounceOff(myPaddle);
}
myPaddle.update();
myBall.update();
}
startGame();
</script>

패들과 공이 충돌했을 때 공이 패들의 어느 부분에 맞았는지에 따라 반사 각도가 달라진다. 패들의 중앙에 맞으면 수직으로, 가장자리에 맞으면 각도가 크게 바뀐다.

브릭 브레이커 바운싱

벽돌을 부수는 브릭 브레이커 스타일의 바운싱 게임을 구현해보자.

<canvas id="myCanvas7" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<script>
var myBall;
var myPaddle;
var myBricks = [];
var myGameArea = {
canvas: document.getElementById("myCanvas7"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function (e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.active = true;
if (type == "ball") {
this.radius = width / 2;
this.speedX = 3;
this.speedY = -3;
}
this.update = function() {
if (!this.active) return;
ctx = myGameArea.context;
if (this.type == "ball") {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x + this.radius, this.y + this.radius, this.radius, 0, 2 * Math.PI);
ctx.fill();
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
if (this.type == "brick") {
ctx.strokeStyle = "white";
ctx.lineWidth = 2;
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
}
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
this.checkBounds = function() {
if (this.type == "ball") {
if (this.x <= 0 || this.x >= myGameArea.canvas.width - this.width) {
this.speedX = -this.speedX;
}
if (this.y <= 0) { this.speedY = -this.speedY; } if (this.y >= myGameArea.canvas.height - this.height) {
this.x = myGameArea.canvas.width / 2 - this.radius;
this.y = myGameArea.canvas.height / 2;
this.speedX = 3;
this.speedY = -3;
}
}
}
this.crashWith = function(otherobj) {
if (!otherobj.active) return false;
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
this.bounceOff = function(otherobj) {
if (this.type == "ball") {
var ballCenterX = this.x + this.radius;
var ballCenterY = this.y + this.radius;
var objCenterX = otherobj.x + otherobj.width / 2;
var objCenterY = otherobj.y + otherobj.height / 2;
var dx = Math.abs(ballCenterX - objCenterX);
var dy = Math.abs(ballCenterY - objCenterY);
if (dx > dy) {
this.speedX = -this.speedX;
} else {
this.speedY = -this.speedY;
}
}
}
}
function startGame() {
myBall = new component(16, 16, "white", 240, 200, "ball");
myPaddle = new component(80, 10, "blue", 200, 250);
for (var row = 0; row < 4; row++) { for (var col = 0; col < 8; col++) { var colors = ["red", "orange", "yellow", "green"]; var brick = new component(55, 20, colors[row], col * 60 + 5, row * 25 + 30, "brick"); myBricks.push(brick); } } myGameArea.start(); } function updateGameArea() { myGameArea.clear(); myPaddle.speedX = 0; if (myGameArea.key && myGameArea.key == 37) {myPaddle.speedX = -4; } if (myGameArea.key && myGameArea.key == 39) {myPaddle.speedX = 4; } myPaddle.newPos(); myBall.newPos(); myBall.checkBounds(); if (myBall.crashWith(myPaddle)) { var ballCenterX = myBall.x + myBall.radius; var paddleCenterX = myPaddle.x + myPaddle.width / 2; var hitPos = (ballCenterX - paddleCenterX) / (myPaddle.width / 2); myBall.speedX = hitPos * 4; myBall.speedY = -Math.abs(myBall.speedY); } for (var i = 0; i < myBricks.length; i++) { if (myBricks[i].active && myBall.crashWith(myBricks[i])) { myBricks[i].active = false; myBall.bounceOff(myBricks[i]); break; } } myPaddle.update(); myBall.update(); for (var i = 0; i < myBricks.length; i++) { myBricks[i].update(); } } startGame(); </script>[/code]

벽돌과 충돌했을 때 충돌 방향을 계산하여 적절한 반사 각도를 만든다. 벽돌은 충돌 후 비활성화되어 사라진다.

중력이 있는 바운싱

중력과 바운싱을 함께 적용한 현실적인 물리 시뮬레이션을 구현해보자.

<canvas id="myCanvas8" width="480" height="270" style="border:1px solid #d3d3d3;">
브라우저가 HTML5 Canvas를 지원하지 않습니다.
</canvas>
<button onclick="addBall()">공 추가</button>
<script>
var myBalls = [];
var myGameArea = {
canvas: document.getElementById("myCanvas8"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(radius, color, x, y) {
this.radius = radius;
this.speedX = (Math.random() - 0.5) * 8;
this.speedY = Math.random() * -5;
this.x = x;
this.y = y;
this.gravity = 0.2;
this.gravitySpeed = 0;
this.bounce = 0.7;
this.friction = 0.98;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
ctx.stroke();
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.speedX *= this.friction;
this.gravitySpeed *= this.friction;
this.x += this.speedX;
this.y += this.speedY + this.gravitySpeed;
this.checkBounds();
}
this.checkBounds = function() {
if (this.x - this.radius <= 0) { this.x = this.radius; this.speedX = -this.speedX * this.bounce; } if (this.x + this.radius >= myGameArea.canvas.width) {
this.x = myGameArea.canvas.width - this.radius;
this.speedX = -this.speedX * this.bounce;
}
if (this.y - this.radius <= 0) { this.y = this.radius; this.speedY = -this.speedY * this.bounce; this.gravitySpeed = -this.gravitySpeed * this.bounce; } if (this.y + this.radius >= myGameArea.canvas.height) {
this.y = myGameArea.canvas.height - this.radius;
if (Math.abs(this.gravitySpeed) > 0.5) {
this.gravitySpeed = -this.gravitySpeed * this.bounce;
} else {
this.gravitySpeed = 0;
}
this.speedY = -this.speedY * this.bounce;
}
}
}
function getRandomColor() {
var colors = ["red", "blue", "green", "purple", "orange", "pink", "yellow", "cyan"];
return colors[Math.floor(Math.random() * colors.length)];
}
function addBall() {
var radius = Math.random() * 15 + 10;
var x = Math.random() * (myGameArea.canvas.width - 2 * radius) + radius;
var y = radius;
var color = getRandomColor();
myBalls.push(new component(radius, color, x, y));
}
function startGame() {
addBall();
addBall();
addBall();
myGameArea.start();
}
function updateGameArea() {
myGameArea.clear();
for (var i = 0; i < myBalls.length; i++) { myBalls[i].newPos(); myBalls[i].update(); } } startGame(); </script>[/code]

중력, 마찰력, 탄성 계수를 모두 적용하여 현실적인 바운싱 볼 시뮬레이션을 만들었다. 공들이 점차 에너지를 잃으며 바닥에서 정지한다.

💡 게임 바운싱 시스템 설계 팁:
• 탄성 계수는 0과 1 사이의 값을 사용한다. 1에 가까울수록 완전 탄성, 0에 가까울수록 비탄성 충돌이다
• 충돌 감지 시 오브젝트가 경계를 벗어나지 않도록 위치를 조정하는 것이 중요하다
• 복잡한 충돌 계산은 성능에 영향을 줄 수 있으므로 필요에 따라 최적화해야 한다
• 마찰력과 중력을 함께 사용하면 더 현실적인 물리 효과를 얻을 수 있다

HTML5 Canvas 게임에서 바운싱 효과는 게임에 역동성과 재미를 더하는 중요한 물리 시뮬레이션이다. 기본적인 벽면 반사부터 복잡한 오브젝트 간 충돌까지 다양한 바운싱 기법을 통해 매력적인 게임을 만들 수 있다.

참고
Mingg`s Diary
밍구
공부 목적 블로그