[TIL] 24.10.09 WED

GDORI·2024년 10월 9일
0

TIL

목록 보기
66/79
post-thumbnail

디렉터리

html에 적혀있는 코드를 살짝 분리시켰다.
public
├─images
└─src
├─css
└─js

클라이언트 스켈레톤 구조

HTML이 index, login, register 세 개로 index는
button-container인 div와 캔버스로 구성되어있고, 처음에는 css로 gameCanvas가 display:none 되어있다.

로그인 , 회원가입 버튼을 누르면 html 전환이 되며, 게임 플레이 버튼을 누르면 div가 display:none 되고, gameCanvas가 block 되는 형식이다.

제공되는 js는 4가지로 game, base, monster, tower이다.

game.js

초반 Base, Monster, Tower 클래스가 담긴 JS를 임포트한다.
웹소켓 객체를 담을 변수와 canvas 관련 변수가 선언되어있다.

let serverSocket; // 서버 웹소켓 객체
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");

게임에서 사용할 몬스터의 수를 변수로 지정하고, 그 수에 맞게
이미지를 불러온다.

const NUM_OF_MONSTERS = 5; // 몬스터 개수
const monsterImages = [];
for (let i = 1; i <= NUM_OF_MONSTERS; i++) {
  const img = new Image();
  img.src = `images/monster${i}.png`;
  monsterImages.push(img);
}

그 외 이미지는 하드 코딩으로 불러온다.

// 이미지 로딩 파트
const backgroundImage = new Image();
backgroundImage.src = "images/bg.webp";

const towerImage = new Image();
towerImage.src = "images/tower.png";

const baseImage = new Image();
baseImage.src = "images/base.png";

const pathImage = new Image();
pathImage.src = "images/path.png";

몬스터가 지나다닐 길의 경우 미리 지정된 것이 아닌 랜덤으로 canvas의 크기를 판단하여 x가 0이고 y는 500~520 사이의 랜덤한 값에서 시작하여 x는 50~150 범위, y는 -100~100 범위로 랜덤하게 설정되어 path를 설정한다.

function generateRandomMonsterPath() {
  const path = [];
  let currentX = 0;
  let currentY = Math.floor(Math.random() * 21) + 500; // 500 ~ 520 범위의 y 시작 (캔버스 y축 중간쯤에서 시작할 수 있도록 유도)

  path.push({ x: currentX, y: currentY });

  while (currentX < canvas.width) {
    currentX += Math.floor(Math.random() * 100) + 50; // 50 ~ 150 범위의 x 증가
    // x 좌표에 대한 clamp 처리
    if (currentX > canvas.width) {
      currentX = canvas.width;
    }

    currentY += Math.floor(Math.random() * 200) - 100; // -100 ~ 100 범위의 y 변경
    // y 좌표에 대한 clamp 처리
    if (currentY < 0) {
      currentY = 0;
    }
    if (currentY > canvas.height) {
      currentY = canvas.height;
    }

    path.push({ x: currentX, y: currentY });
  }

  return path;
}

위에서 얻은 path의 x,y값을 토대로 path image를 그리는 함수가 있다. 현재의 path좌표와 다음 path 좌표의 거리 변화를 측정하여 두 점 사이의 유클리드 거리를 구하고 Math.atan2를 이용하여 두 점 사이의 각도를 계산합니다.

function drawPath() {
  const segmentLength = 20; // 몬스터 경로 세그먼트 길이
  const imageWidth = 60; // 몬스터 경로 이미지 너비
  const imageHeight = 60; // 몬스터 경로 이미지 높이
  const gap = 5; // 몬스터 경로 이미지 겹침 방지를 위한 간격

  for (let i = 0; i < monsterPath.length - 1; i++) {
    const startX = monsterPath[i].x;
    const startY = monsterPath[i].y;
    const endX = monsterPath[i + 1].x;
    const endY = monsterPath[i + 1].y;

    const deltaX = endX - startX;
    const deltaY = endY - startY;
    const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // 피타고라스 정리로 두 점 사이의 거리를 구함 (유클리드 거리)
    const angle = Math.atan2(deltaY, deltaX); // 두 점 사이의 각도는 tan-1(y/x)로 구해야 함 (자세한 것은 역삼각함수 참고): 삼각함수는 변의 비율! 역삼각함수는 각도를 구하는 것!

    for (let j = gap; j < distance - gap; j += segmentLength) {
      // 사실 이거는 삼각함수에 대한 기본적인 이해도가 있으면 충분히 이해하실 수 있습니다.
      // 자세한 것은 https://thirdspacelearning.com/gcse-maths/geometry-and-measure/sin-cos-tan-graphs/ 참고 부탁해요!
      const x = startX + Math.cos(angle) * j; // 다음 이미지 x좌표 계산(각도의 코사인 값은 x축 방향의 단위 벡터 * j를 곱하여 경로를 따라 이동한 x축 좌표를 구함)
      const y = startY + Math.sin(angle) * j; // 다음 이미지 y좌표 계산(각도의 사인 값은 y축 방향의 단위 벡터 * j를 곱하여 경로를 따라 이동한 y축 좌표를 구함)
      drawRotatedImage(pathImage, x, y, imageWidth, imageHeight, angle);
    }
  }
}

function drawRotatedImage(image, x, y, width, height, angle) {
  ctx.save();
  ctx.translate(x + width / 2, y + height / 2);
  ctx.rotate(angle);
  ctx.drawImage(image, -width / 2, -height / 2, width, height);
  ctx.restore();
}

그 외 타워 배치 시 랜덤으로 배치가 되기 때문에 경로 근처
랜덤 위치 좌표를 반환하는 함수,
초기 타워 배치하는 함수, 타워 구매 후 랜덤 배치하는 함수,
몬스터 경로 배열 마지막 요소 좌표를 기준으로 Base를 생성하는 함수 , 몬스터를 생성하는 함수, 게임루프 함수 등 구현해야하는 것이 많다.

공부할게 너무 많다.

게임 분야는 수학도 잘 알아야 하는 것 같다.. 머리가 아프다.
우선 우리 팀의 방향은 서버중심적 프로그래밍으로 전환하기로 했는데, 이 스켈레톤을 어떻게 뜯어 고쳐야 할 지 지끈지끈 하다 ㅋㅋ

우선은 간단하게 삼각함수부터...

정승제 선생님 유튜브

오랜만에 듣는 수학강의인데 너무 쉽게 재밌게 가르쳐주신다.
어릴 때 좀 들을걸..😂

profile
하루 최소 1시간이라도 공부하자..

0개의 댓글