VUE+Canvas 데스크 톱 당구 벽돌 제거 게임 의 예제 코드 구현

모두 가 공 을 튕 기 고 벽돌 을 없 애 는 놀 이 를 한 적 이 있다.왼쪽 과 오른쪽 버튼 으로 맨 밑 에 있 는 작은 널 빤 지 를 평평 하 게 이동 시 키 고 떨 어 진 작은 공 을 받 아 공 을 튕 긴 후에 화면 위의 벽돌 더 미 를 없앤다.
그러면 VUE+Canvas 로 어떻게 이 루어 질 까요?실현 방향 은 매우 간단 하 다.먼저 캔버스 에 그 려 야 할 내용 을 나 누 어 보 자.
(1)키보드 좌우 버튼 으로 이동 하 는 널 빤 지 를 제어 한다.
(2)캔버스 안에서 사방 으로 튀 는 작은 공;
(3)화면 위 에 고정 하고 공 에 부 딪 히 면 사라 지 는 벽돌 더미.
상기 세 가지 대상 을 requestAnimationFrame()함수 로 이동 시 킨 다음 에 각종 충돌 검 사 를 결합 하면 최종 결 과 를 얻 을 수 있다.
최종 효과 부터 살 펴 보 자.

1.좌우 로 평평 하 게 옮 긴 널빤지
맨 밑 에 있 는 널 빤 지 는 가장 간단 한 부분 입 니 다.널빤지 의 y 좌 표 는 고정 되 어 있 기 때문에 우 리 는 널빤지 의 초기 매개 변 수 를 설정 합 니 다.너비,높이,이동 속도 등 을 포함 하고 널빤지 의 함 수 를 실현 합 니 다.

pannel: {
        x: 0,
        y: 0,
        height: 8,
        width: 100,
        speed: 8,
        dx: 0
},
 
....
 
drawPannel() {
      this.drawRoundRect(
        this.pannel.x,
        this.pannel.y,
        this.pannel.width,
        this.pannel.height,
        5
      );
},
drawRoundRect(x, y, width, height, radius) { //      
      this.ctx.beginPath();
      this.ctx.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
      this.ctx.lineTo(width - radius + x, y);
      this.ctx.arc(
        width - radius + x,
        radius + y,
        radius,
        (Math.PI * 3) / 2,
        Math.PI * 2
      );
      this.ctx.lineTo(width + x, height + y - radius);
      this.ctx.arc(
        width - radius + x,
        height - radius + y,
        radius,
        0,
        (Math.PI * 1) / 2
      );
      this.ctx.lineTo(radius + x, height + y);
      this.ctx.arc(
        radius + x,
        height - radius + y,
        radius,
        (Math.PI * 1) / 2,
        Math.PI
      );
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
}
프로그램 이 초기 화 되 었 을 때 키보드 의 좌우 방향 키 를 감청 하여 널 빤 지 를 이동 하고 길 이 를 통 해 좌우 경계 로 이동 하여 화면 을 계속 이동 할 수 없 는 지 판단 합 니 다.

document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if (key === 37) {
        //   
        _this.pannel.dx = -_this.pannel.speed;
      } else if (key === 39) {
        //   
        _this.pannel.dx = _this.pannel.speed;
      }
};
document.onkeyup = function(e) {
      _this.pannel.dx = 0;
};
....
 
 movePannel() {
      this.pannel.x += this.pannel.dx;
      if (this.pannel.x > this.clientWidth - this.pannel.width) {
        this.pannel.x = this.clientWidth - this.pannel.width;
      } else if (this.pannel.x < 0) {
        this.pannel.x = 0;
      }
},
2.점프 하 는 작은 공 과 충돌 검 측
작은 공의 운동 은 널빤지 와 유사 하지만 dx 의 오프셋 뿐만 아니 라 dy 의 오프셋 도 있다.
그리고 충돌 검사 도 있어 야 합 니 다.
(1)위,오른쪽,왼쪽 벽 과 널빤지 에 부 딪 혔 을 때 튕 겨 나온다.
(2)널빤지 이외 의 하 경계 에 부 딪 혔 을 때 게임 에서 진다.
(3)벽돌 에 부 딪 혔 을 때 부 딪 힌 벽돌 이 사라 지고 점수+1 로 작은 공이 튕 겨 나온다.
그래서 널빤지 와 마찬가지 로 작은 공 부분 을 작은 공 함수 drawBall()과 작은 공 운동 함수 moveBall()로 나 누 었 다.

drawBall() {
      this.ctx.beginPath();
      this.ctx.arc(this.ball.x, this.ball.y, this.ball.r, 0, 2 * Math.PI);
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
},
moveBall() {
      this.ball.x += this.ball.dx;
      this.ball.y += this.ball.dy;
      this.breaksHandle();
      this.edgeHandle();
},
breaksHandle() {
      //       
      this.breaks.forEach(item => {
        if (item.show) {
          if (
            this.ball.x + this.ball.r > item.x &&
            this.ball.x - this.ball.r < item.x + this.breaksConfig.width &&
            this.ball.y + this.ball.r > item.y &&
            this.ball.y - this.ball.r < item.y + this.breaksConfig.height
          ) {
            item.show = false;
            this.ball.dy *= -1;
            this.score ++ ;
            if(this.showBreaksCount === 0){
              this.gameOver = true;
            }
          }
        }
      });
},
edgeHandle() {
      //     
      //       
      if (this.ball.y - this.ball.r < 0) {
        this.ball.dy = -this.ball.dy;
      }
      if (
        //       
        this.ball.x - this.ball.r < 0 ||
        this.ball.x + this.ball.r > this.clientWidth
      ) {
        this.ball.dx = -this.ball.dx;
      }
      if (
        this.ball.x >= this.pannel.x &&
        this.ball.x <= this.pannel.x + this.pannel.width &&
        this.ball.y + this.ball.r >= this.clientHeight - this.pannel.height
      ) {
        //   x             
        this.ball.dy *= -1;
      } else if (
        (this.ball.x < this.pannel.x ||
          this.ball.x > this.pannel.x + this.pannel.width) &&
        this.ball.y + this.ball.r >= this.clientHeight
      ) {
        //         
        this.gameOver = true;
        this.getCurshBreaks();
      }
}
3.벽돌의 생 성
벽돌의 생 성도 비교적 간단 하 다.여기 서 우 리 는 약간의 데 이 터 를 시작 했다.

breaksConfig: {
        row: 6, //   
        height: 25, //     
        width: 130, //     
        radius: 5, //     
        space: 0, //   
        colunm: 6 //   
}
이러한 배치 항목 과 캔버스 너비 에 따라 우 리 는 모든 벽돌 의 가로 간격 이 얼마 인지 계산 할 수 있다.

//           
      this.breaksConfig.space = Math.floor(
        (this.clientWidth -
          this.breaksConfig.width * this.breaksConfig.colunm) /
          (this.breaksConfig.colunm + 1)
      );
그래서 우 리 는 모든 벽돌 이 캔버스 에 있 는 x,y 좌 표를 얻 을 수 있다.

for (let i = 0; i < _this.breaksConfig.row; i++) {
        for (let j = 0; j < _this.breaksConfig.colunm; j++) {
          _this.breaks.push({
            x: this.breaksConfig.space * (j + 1) + this.breaksConfig.width * j,
            y: 10 * (i + 1) + this.breaksConfig.height * i,
            show: true
          });
        }
      }
게다가 벽돌 을 그 리 는 함수:

drawBreaks() {
      let _this = this;
      _this.breaks.forEach(item => {
        if (item.show) {
          _this.drawRoundRect(
            item.x,
            item.y,
            _this.breaksConfig.width,
            _this.breaksConfig.height,
            _this.breaksConfig.radius
          );
        }
      });
}
4.위의 세 부분 을 움 직 이게 합 니 다.

(function animloop() {
      if (!_this.gameOver) {
        _this.movePannel();
        _this.moveBall();
        _this.drawAll();
      } else {
        _this.drawCrushBreaks();
      }
      window.requestAnimationFrame(animloop);
})();
....
 drawAll() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.drawPannel();
      this.drawBall();
      this.drawScore();
      this.drawBreaks();
}
5.게임 종료 후 효과
처음에 움 직 이 는 그림 에서 볼 수 있 듯 이 게임 이 끝 난 후에 벽돌 은 몇 개의 작은 공 으로 갈 라 져 떨 어 졌 다.이것 은 사실은 단독 적 인 작은 공 을 그 리 는 것 과 비슷 하 다.생각 은 바로 남 은 벽돌 중심 좌표 에서 몇 개의 크기 가 다 르 고 운동 궤적 이 다 르 며 색깔 이 다른 작은 공 을 생산 한 다음 에 애니메이션 을 계속 하 는 것 이다.

getCurshBreaks() {
      let _this = this;
      this.breaks.forEach(item => {
        if (item.show) {
          item.show = false;
          for (let i = 0; i < 8; i++) { //        8   
            this.crushBalls.push({
              x: item.x + this.breaksConfig.width / 2,
              y: item.y + this.breaksConfig.height / 2,
              dx: _this.getRandomArbitrary(-6, 6),
              dy: _this.getRandomArbitrary(-6, 6),
              r: _this.getRandomArbitrary(1, 4),
              color: _this.getRandomColor()
            });
          }
        }
      });
},
drawCrushBreaks() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.crushBalls.forEach(item => {
        this.ctx.beginPath();
        this.ctx.arc(item.x, item.y, item.r, 0, 2 * Math.PI);
        this.ctx.fillStyle = item.color;
        this.ctx.fill();
        this.ctx.closePath();
        item.x += item.dx;
        item.y += item.dy;
        if (
          //       
          item.x - item.r < 0 ||
          item.x + item.r > this.clientWidth
        ) {
          item.dx = -item.dx;
        }
        if (
          //       
          item.y - item.r < 0 ||
          item.y + item.r > this.clientHeight
        ) {
          item.dy = -item.dy;
        }
      });
},
이상 은 바로 데스크 톱 에서 공 을 튕 기 고 벽돌 을 없 애 는 작은 게임 의 실현 방향 과 일부 코드 입 니 다.실현 하기 가 매우 간단 합 니 다.이 300 줄 의 코드 는 이 작은 게임 을 실현 할 수 있 습 니 다.작은 공의 운동 에 있어 서 지속 적 으로 최적화 할 수 있 고 난이도 옵션 조작 도 증가 할 수 있다.
마지막 으로 모든 vue 파일 코드 를 첨부 하여 여러분 이 참고 하여 공부 할 수 있 도록 합 니 다.

<template>
  <div class="break-ball">
    <canvas id="breakBall" width="900" height="600"></canvas>
    <div class="container" v-if="gameOver">
      <div class="dialog">
        <p class="once-again">    :{{score}} </p>
        <p class="once-again">   !</p>
        <p class="once-again">    ~~</p>
        <el-button class="once-again-btn" @click="init">  </el-button>
      </div>
    </div>
  </div>
</template>
 
<script>
const randomColor = [
  "#FF95CA",
  "#00E3E3",
  "#00E3E3",
  "#6F00D2",
  "#6F00D2",
  "#C2C287",
  "#ECFFFF",
  "#FFDC35",
  "#93FF93",
  "#d0d0d0"
];
export default {
  name: "BreakBall",
  data() {
    return {
      clientWidth: 0,
      clientHeight: 0,
      ctx: null,
      crushBalls: [],
      pannel: {
        x: 0,
        y: 0,
        height: 8,
        width: 100,
        speed: 8,
        dx: 0
      },
      ball: {
        x: 0,
        y: 0,
        r: 8,
        dx: -4,
        dy: -4
      },
      score: 0,
      gameOver: false,
      breaks: [],
      breaksConfig: {
        row: 6, //   
        height: 25, //     
        width: 130, //     
        radius: 5, //     
        space: 0, //   
        colunm: 6 //   
      }
    };
  },
  mounted() {
    let _this = this;
    let container = document.getElementById("breakBall");
    this.ctx = container.getContext("2d");
    this.clientHeight = container.height;
    this.clientWidth = container.width;
    _this.init();
    document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if (key === 37) {
        //   
        _this.pannel.dx = -_this.pannel.speed;
      } else if (key === 39) {
        //   
        _this.pannel.dx = _this.pannel.speed;
      }
    };
    document.onkeyup = function(e) {
      _this.pannel.dx = 0;
    };
    (function animloop() {
      if (!_this.gameOver) {
        _this.movePannel();
        _this.moveBall();
        _this.drawAll();
      } else {
        _this.drawCrushBreaks();
      }
      window.requestAnimationFrame(animloop);
    })();
  },
  computed:{
    showBreaksCount(){
      return this.breaks.filter(item=>{
        return item.show;
      }).length;
    }
  },
  methods: {
    init() {
      let _this = this;
      _this.gameOver = false;
      this.pannel.y = this.clientHeight - this.pannel.height;
      this.pannel.x = this.clientWidth / 2 - this.pannel.width / 2;
      this.ball.y = this.clientHeight / 2;
      this.ball.x = this.clientWidth / 2;
      this.score = 0;
      this.ball.dx = [-1,1][Math.floor(Math.random() * 2)]*4;
      this.ball.dy = [-1,1][Math.floor(Math.random() * 2)]*4;
      this.crushBalls = [];
      this.breaks = [];
      //           
      this.breaksConfig.space = Math.floor(
        (this.clientWidth -
          this.breaksConfig.width * this.breaksConfig.colunm) /
          (this.breaksConfig.colunm + 1)
      );
 
      for (let i = 0; i < _this.breaksConfig.row; i++) {
        for (let j = 0; j < _this.breaksConfig.colunm; j++) {
          _this.breaks.push({
            x: this.breaksConfig.space * (j + 1) + this.breaksConfig.width * j,
            y: 10 * (i + 1) + this.breaksConfig.height * i,
            show: true
          });
        }
      }
    },
    drawAll() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.drawPannel();
      this.drawBall();
      this.drawScore();
      this.drawBreaks();
    },
    movePannel() {
      this.pannel.x += this.pannel.dx;
      if (this.pannel.x > this.clientWidth - this.pannel.width) {
        this.pannel.x = this.clientWidth - this.pannel.width;
      } else if (this.pannel.x < 0) {
        this.pannel.x = 0;
      }
    },
    moveBall() {
      this.ball.x += this.ball.dx;
      this.ball.y += this.ball.dy;
      this.breaksHandle();
      this.edgeHandle();
    },
    breaksHandle() {
      //       
      this.breaks.forEach(item => {
        if (item.show) {
          if (
            this.ball.x + this.ball.r > item.x &&
            this.ball.x - this.ball.r < item.x + this.breaksConfig.width &&
            this.ball.y + this.ball.r > item.y &&
            this.ball.y - this.ball.r < item.y + this.breaksConfig.height
          ) {
            item.show = false;
            this.ball.dy *= -1;
            this.score ++ ;
            if(this.showBreaksCount === 0){
              this.gameOver = true;
            }
          }
        }
      });
    },
    edgeHandle() {
      //     
      //       
      if (this.ball.y - this.ball.r < 0) {
        this.ball.dy = -this.ball.dy;
      }
      if (
        //       
        this.ball.x - this.ball.r < 0 ||
        this.ball.x + this.ball.r > this.clientWidth
      ) {
        this.ball.dx = -this.ball.dx;
      }
      if (
        this.ball.x >= this.pannel.x &&
        this.ball.x <= this.pannel.x + this.pannel.width &&
        this.ball.y + this.ball.r >= this.clientHeight - this.pannel.height
      ) {
        //   x             
        this.ball.dy *= -1;
      } else if (
        (this.ball.x < this.pannel.x ||
          this.ball.x > this.pannel.x + this.pannel.width) &&
        this.ball.y + this.ball.r >= this.clientHeight
      ) {
        //         
        this.gameOver = true;
        this.getCurshBreaks();
      }
    },
    drawScore(){
      this.ctx.beginPath();
      this.ctx.font="14px Arial";
      this.ctx.fillStyle = "#FFF";
      this.ctx.fillText("  :"+this.score,10,this.clientHeight-14);
      this.ctx.closePath();
    },
    drawCrushBreaks() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.crushBalls.forEach(item => {
        this.ctx.beginPath();
        this.ctx.arc(item.x, item.y, item.r, 0, 2 * Math.PI);
        this.ctx.fillStyle = item.color;
        this.ctx.fill();
        this.ctx.closePath();
        item.x += item.dx;
        item.y += item.dy;
        if (
          //       
          item.x - item.r < 0 ||
          item.x + item.r > this.clientWidth
        ) {
          item.dx = -item.dx;
        }
        if (
          //       
          item.y - item.r < 0 ||
          item.y + item.r > this.clientHeight
        ) {
          item.dy = -item.dy;
        }
      });
    },
    getRandomColor() {
      return randomColor[Math.floor(Math.random() * randomColor.length)];
    },
    getRandomArbitrary(min, max) {
      return Math.random() * (max - min) + min;
    },
    getCurshBreaks() {
      let _this = this;
      this.breaks.forEach(item => {
        if (item.show) {
          item.show = false;
          for (let i = 0; i < 8; i++) {
            this.crushBalls.push({
              x: item.x + this.breaksConfig.width / 2,
              y: item.y + this.breaksConfig.height / 2,
              dx: _this.getRandomArbitrary(-6, 6),
              dy: _this.getRandomArbitrary(-6, 6),
              r: _this.getRandomArbitrary(1, 4),
              color: _this.getRandomColor()
            });
          }
        }
      });
    },
    drawBall() {
      this.ctx.beginPath();
      this.ctx.arc(this.ball.x, this.ball.y, this.ball.r, 0, 2 * Math.PI);
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
    },
    drawPannel() {
      this.drawRoundRect(
        this.pannel.x,
        this.pannel.y,
        this.pannel.width,
        this.pannel.height,
        5
      );
    },
    drawRoundRect(x, y, width, height, radius) {
      this.ctx.beginPath();
      this.ctx.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
      this.ctx.lineTo(width - radius + x, y);
      this.ctx.arc(
        width - radius + x,
        radius + y,
        radius,
        (Math.PI * 3) / 2,
        Math.PI * 2
      );
      this.ctx.lineTo(width + x, height + y - radius);
      this.ctx.arc(
        width - radius + x,
        height - radius + y,
        radius,
        0,
        (Math.PI * 1) / 2
      );
      this.ctx.lineTo(radius + x, height + y);
      this.ctx.arc(
        radius + x,
        height - radius + y,
        radius,
        (Math.PI * 1) / 2,
        Math.PI
      );
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
    },
    drawBreaks() {
      let _this = this;
      _this.breaks.forEach(item => {
        if (item.show) {
          _this.drawRoundRect(
            item.x,
            item.y,
            _this.breaksConfig.width,
            _this.breaksConfig.height,
            _this.breaksConfig.radius
          );
        }
      });
    }
  }
};
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.break-ball {
  width: 900px;
  height: 600px;
  position: relative;
  #breakBall {
    background: #2a4546;
  }
  .container {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.3);
    text-align: center;
    font-size: 0;
    white-space: nowrap;
    overflow: auto;
  }
  .container:after {
    content: "";
    display: inline-block;
    height: 100%;
    vertical-align: middle;
  }
  .dialog {
    width: 400px;
    height: 300px;
    background: rgba(255, 255, 255, 0.5);
    box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.3);
    display: inline-block;
    vertical-align: middle;
    text-align: left;
    font-size: 28px;
    color: #fff;
    font-weight: 600;
    border-radius: 10px;
    white-space: normal;
    text-align: center;
    .once-again-btn {
      background: #1f9a9a;
      border: none;
      color: #fff;
    }
  }
}
</style>
VUE+Canvas 가 데스크 톱 에서 공 을 튕 기 고 벽돌 을 없 애 는 작은 게임 을 실현 하 는 것 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.vue 공 을 튕 기 고 벽돌 을 없 애 는 작은 게임 에 관 한 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 읽 어 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기