221102 HTML #5

김혜진·2022년 11월 2일
0

HTML5

목록 보기
5/5

HTML5 차트

https://www.rgraph.net/


게임 구현하기


구조 설계

  • 머리 속에 있는 게임 구현에 대한 구상을 직접 설계하여 문서화하는 단계이다.

DC(Device Context)
그래픽 카드의 디바이스 드라이버와 GDI 함수를 연결해주는 역할

우리가 그림을 그리려면 DC가 필요
그래서 DC를 얻어오기위해 조작함

메모리 DC

메모리에 그림 출력 후 화면에 고속 복사하여 이미지로 출력
이미지를 바로 화면에 출력 시 화면 깜빡임과 같은 매끄럽지 못한 문제 발생
메모리 DC 사용 이유 : 이미지의 용량때문

유닛과 장애물 충돌 계산하기

  • 충돌계산을 하는 핵심 코드는 다음과 같다.

  • mx 및 my의 좌표 범위가 유닛의 범위 안에 들어오면 충돌한 것으로 간주한다는 내용이다.

    sx - sw/2 = 왼쪽 경계
    sy - sh/2 = 위쪽 경계

예제

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>잠수함게임</title>

  <style>
    body {
      background-color: #000000;
      margin: 0px;
    }

    canvas {
      background-color: #0099FF;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width="900" height="600"></canvas>

  <script>
    // 캔버스 객체(변수)
    var canvas;
    var ctx;
    var canvasBuffer;
    var bufferCtx;
    var threadSpeed = 16;

    // 잠수함
    var submarine;
    var sx, sy, sw = 60, sh = 35;

    // 배경이미지
    var background;

    // 장애물
    var enemy = new Array();
    var enemycolor = ["red", "blue", "white"];
    var ellapse = 10;

    // 타이머
    var loopinstance;

    // 게임의 상태
    var STATE_START = false;
    var STATE_GAMEOVER = false;

    // 키 상태
    var keypressed = [];

    // 경과시간(스코어)
    var oldtime;
    var starttime;
    var totaltime;

    // 이벤트 등록하기
    // load : 게임 초기화
    // keydown, keyup : 캐릭터 이동

    window.addEventListener("load", initialize, false);
    window.addEventListener("keydown", getKeyDown, false);
    window.addEventListener("keyup", getKeyUp, false);

    function startMessage()
    {
      drawText(ctx, "Enter to Start", canvas.width/2, canvas.height/2 - 60, "bold 30px arial", "#ffff00", "center", "top")
      drawText(ctx, "조작: 방향키 ←↑→↓", canvas.width/2, canvas.height/2 - 20, "bold 20px arial", "#ffffff", "center", "top")
    }

    function drawText(ctx, text, x, y, font, color, align, base)
    {
      if(font != undefined) ctx.font = font;
      if(color != undefined) ctx.fillStyle = color;
      if(align != undefined) ctx.textAlign = align;
      if(base != undefined) ctx.textBaseline = base;
      ctx.fillText(text, x, y);
    }

    function initialize()
    {
      canvas = document.getElementById("canvas");
      if(canvas == null || canvas.getContext == null) return;
      ctx = canvas.getContext("2d"); // DC발행1

      // 존재하지 않는 요소를 만들어 canvas와 똑같은 크기로 DC발행
      canvasBuffer = document.createElement("canvas") 
      canvasBuffer.width = canvas.width;
      canvasBuffer.height = canvas.height;
      bufferCtx = canvasBuffer.getContext("2d"); // DC발행2

      // 게임 시작 메시지
      startMessage();

      // 이미지 설정
      setImage();

      // 반복 동작 설정
      // setInterval(반복할 함수, 시간) 내가 설정한 주기만큼 반복 실행
      loopinstance = setInterval(update, threadSpeed);
    }

    // 주기적으로 반복되는 루틴
    function update()
    {
      if(keypressed[13] == true && STATE_START == false)
      {
        // 게임을 시작해
        startGame();
      }

      if(keypressed[38]) // 위쪽
      {
          sy = sy - 3;
      }
      if(keypressed[40]) // 아래쪽
      {
        sy = sy + 3;
      }
      if(keypressed[37]) // 왼쪽
      {
        sx = sx - 3; 
      }
      if(keypressed[39]) // 오른쪽
      {
        sx = sx + 3;
      }

      if(keypressed[32] == true)
      {
        document.location.reload();
        startGame();
      }

      if(STATE_START)
      {
        // 장애물 이동
        moveObstacle(ellapse);
        drawAll();
      }
    }

    function moveObstacle(ellapse)
    {
      for(var i = 0; i < 60; i++)
      {
        var mx = enemy[i].vx * ellapse / 1000;
        var my = enemy[i].vy * ellapse / 1000;
        enemy[i].x = enemy[i].x + mx;
        enemy[i].y = enemy[i].y + my;
  
        if(enemy[i].x > canvas.width) enemy[i].x = 0;
        if(enemy[i].x < 0) enemy[i].x = canvas.width;
        if(enemy[i].y > canvas.height) enemy[i].y = 0;
        if(enemy[i].y < 0) enemy[i].y = canvas.height;
  
        // 충돌 검사
        crashObstacle(i);
      }
    }

    function crashObstacle(index)
    {
      var ex = enemy[index].x;
      var ey = enemy[index].y;
      
      if(ex > sx - sw / 2 && ex < sx + sw / 2 
        && ey > sy - sh / 2 && ey < sy + sh / 2)
      {
        STATE_GAMEOVER = true;
      }
    }

    function getTime()
    {
      var date = new Date();
      var time = date.getTime();
      delete date;
      return time;
    }

    function drawAll()
    {
      if(STATE_START == false)
      {
        return;
      }
      else if(STATE_GAMEOVER == true)
      {
        STATE_START = false;
        drawText(ctx, "Game Over", canvas.width/2, canvas.height/2 - 60, "bold 30px arial", "#ffff00", "center", "top")
        drawText(ctx, "Spacebar to Restart", canvas.width/2, canvas.height/2 - 20, "bold 20px arial", "#ffffff", "center", "top")
      }
      else
      {
        // 배경이미지
        drawBk();
        // 잠수함 캐릭터
        drawPlayer();
        ctx.drawImage(canvasBuffer, 0, 0);

        // 장애물 출력
        drawObstacle();

        // 경과시간 출력
        totaltime = (getTime() - starttime)
        drawText(ctx, totaltime, canvas.width - 10, 10, "20px arial", "yellow", "right", "top")
      }
    }

    function drawObstacle()
    {
      for(var i = 0; i < 60; i++)
      {
        ctx.beginPath();
        ctx.arc(enemy[i].x, enemy[i].y, 5, 0, 2*Math.PI);
        ctx.fillStyle = enemycolor[enemy[i].color];
        ctx.closePath();
        ctx.fill();
      }
    }

    function drawBk()
    {
      bufferCtx.drawImage(background, 0, 0);
    }

    function drawPlayer()
    {
      bufferCtx.drawImage(submarine, sx, sy);
    }


    function startGame()
    {
      STATE_START = true;
      sx = canvas.width/2;
      sy = canvas.height/2;
      sw = 60;
      sh = 35;

      // 장애물 생성
      createObstacle();

      starttime = getTime();
    }

    function createObstacle()
    {
      enemy.length = 0;

      for (var i = 0; i < 60; i++)
      {
        
        enemy.push({
          x : Math.random() * canvas.width,
          y : (i < 30 ? 20 : canvas.height - 20),
          vx : Math.random() * 200 - 100,
          vy : Math.random() * 200 - 100,
          color : Math.floor(Math.random() * 3)
        });
      }
    }

    function setImage()
    {
      submarine = new Image();
      submarine.src = "jamsuham.png";
      background = new Image();
      background.src = "sea.jpg";
    }

    function getKeyDown(event)
    {
      keypressed[event.keyCode] = true;
    }

    function getKeyUp()
    {
      keypressed[event.keyCode] = false;
    }
  </script>
</body>
</html>

profile
알고 쓰자!

1개의 댓글

comment-user-thumbnail
2022년 11월 2일

대박 잘 보고 갑니다!!

답글 달기
Powered by GraphCDN, the GraphQL CDN