VUE+Canvas implements the game of God of Wealth receiving ingots

VUE+Canvas implements the game of God of Wealth receiving ingots

Welcome to the previous canvas game series:

《VUE implements a Flappy Bird~~~》

Guess the Word Game

《VUE+Canvas realizes desktop pinball brick-breaking game》

《VUE+Canvas implements a typing game of Thunder Fighter》

As the title suggests, everyone has played this game and it can be seen everywhere. Use the left and right direction keys to control the movement of the God of Wealth and catch the gold ingots falling from the sky. When the time is up, the game ends. Let’s take a look at the effect first:

Compared to the previous Thunder Fighter that fired bullets flying everywhere, the movement trajectory of the elements this time is very simple, with vertical jewels and horizontally moving God of Wealth. Similar to the previous code, here are the key steps:

1. The God of Wealth moves horizontally with keyboard control

This is very simple, similar to the control of the slider in "VUE+Canvas to realize the desktop pinball brick elimination game":

drawCaishen() {
      let _this = this;
      _this.ctx.save();
      _this.ctx.drawImage(
        _this.caishenImg,
        _this.caishen.x,
        _this.caishen.y,
        120,
        120
      );
      _this.ctx.restore();
},
moveCaishen() {
      this.caishen.x += this.caishen.dx;
      if (this.caishen.x > this.clientWidth - 120) {
        this.caishen.x = this.clientWidth - 120;
      } else if (this.caishen.x < 0) {
        this.caishen.x = 0;
      }
}

2. Jewels that fall from the sky

This is also very simple, but it should be noted that the initial x value of the jewels cannot be randomly taken from 0 to clientWidth, because it is easy to cause the jewels to pile up together, affecting the playability of the game, so the jewels are best scattered on different tracks. Here we divide the canvas width into 5 tracks. When the jewels are initially drawn, we scatter them on the track, and the y value is randomly set at a certain height to create a staggered appearance. Then the newly generated jewels are generated according to the track distribution to avoid the jewels being crowded together.

generateTreasure() {
      let _this = this;
      if (_this.treasureArr.length < MaxNum) {
        let random = Math.floor(Math.random() * TreasureNames.length);
        let channel = _this.getRandomArbitrary(1, 5);
        _this.treasureArr.push({
          x: _this.channelWidth * (1 / 2 + (channel - 1)) - 30,
          y: 0,
          name: TreasureNames[random],
          speed: _this.getRandomArbitrary(2, 4)
        });
      }
},
filterTreasure(item) {
      let _this = this;
      if (
        item.x <= _this.caishen.x + 110 &&
        item.x >= _this.caishen.x &&
        item.y > _this.caishen.y
      ) {
        // Determine the touching range with the God of Wealth_this.score += _this.treasureObj[item.name].score;
        return false;
      }
      if (item.y >= _this.clientHeight) {
        return false;
      }
      return true;
},
drawTreasure() {
      let _this = this;
      _this.treasureArr = _this.treasureArr.filter(_this.filterTreasure);
      _this.treasureArr.forEach(item => {
        _this.ctx.drawImage(
          _this.treasureObj[item.name].src,
          item.x,
          item.y,
          60,
          60
        );
        item.y += item.speed;
      });
},
getRandomArbitrary(min, max) {
      return Math.random() * (max - min) + min;
}

Here we use the filter function to filter out the jewels that should disappear. If we use the for+splice+i-- method, it will cause jitter.

Then give each jewel a random movement speed, and when the jewel enters the picture range of the God of Wealth, the corresponding score is accumulated.

3. Countdown ring

Set the countdown to 30s. Then in the requestAnimationFrame callback, calculate whether the difference in milliseconds between the current time and the last timestamp is greater than 1000 to calculate the seconds. Then take another timestamp to accumulate progress to achieve smooth movement of the ring.

drawCountDown() {
      // Draw the progress ring let _this = this;
      _this.progress += Date.now() - _this.timeTag2;
      _this.timeTag2 = Date.now();
      _this.ctx.beginPath();
      _this.ctx.moveTo(50, 50);
      _this.ctx.arc(
        50,
        50,
        40,
        Math.PI * 1.5,
        Math.PI * (1.5 + 2 * (_this.progress / (countDownInit * 1000))),
        false
      );
      _this.ctx.closePath();
      _this.ctx.fillStyle = "yellow";
      _this.ctx.fill();
 
      // Draw a filled circle_this.ctx.beginPath();
      _this.ctx.arc(50, 50, 30, 0, Math.PI * 2);
      _this.ctx.closePath();
      _this.ctx.fillStyle = "#fff";
      _this.ctx.fill();
 
      //Fill text_this.ctx.font = "bold 16px Microsoft YaHei";
      _this.ctx.fillStyle = "#333";
      _this.ctx.textAlign = "center";
      _this.ctx.textBaseline = "middle";
      _this.ctx.moveTo(50, 50);
      _this.ctx.fillText(_this.countDown + "s", 50, 50);
    }
(function animloop() {
        _this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight);
        _this.loop();
        animationId = window.requestAnimationFrame(animloop);
        if (_this.countDown === 0) {
          _this.gameOver = true;
          window.cancelAnimationFrame(animationId);
        }
        if (Date.now() - _this.timeTag >= 1000) {
          _this.countDown--;
          _this.timeTag = Date.now();
        }
})();

At this point, a very simple game of the God of Wealth catching gold ingots has been completed. Of course, in order to increase the difficulty, you can set up a segment of dropping bombs continuously. The principle is the same as the movement of jewels.

The following is the full code for your reference:

<template>
  <div class="caishen">
    <canvas id="caishen" width="1200" height="750"></canvas>
    <div class="container" v-if="gameOver">
      <div class="dialog">
        <p class="once-again">Congratulations! </p>
        <p class="once-again">Treasures in this round: {{ score }} points</p>
      </div>
    </div>
  </div>
</template>
 
<script>
const TreasureNames = [
  "yuanbao",
  "tongqian",
  "jintiao",
  "shuijin_red",
  "shuijin_blue",
  "fudai"
];
let animationId = null;
let countDownInit = 0;
const MaxNum = 5;
export default {
  name: "CaiShen",
  data() {
    return {
      score: 0,
      ctx: null,
      caishenImg: null,
      clientWidth: 0,
      clientHeight: 0,
      channelWidth: 0,
      caishen:
        x: 0,
        y: 0,
        speed: 8,
        dx: 0
      },
      progress: 0,
      countDown: 30,
      timeTag: Date.now(),
      timeTag2: Date.now(),
      treasureArr: [],
      gameOver: false,
      treasureObj: {
        yuanbao:
          score: 5,
          src: null
        },
        Tongqian:
          score: 2,
          src: null
        },
        jintiao:
          score: 10,
          src: null
        },
        shuijin_red:
          score: 20,
          src: null
        },
        shuijin_blue: {
          score: 15,
          src: null
        },
        fudai:
          score: 8,
          src: null
        }
      }
    };
  },
  mounted() {
    let _this = this;
    let container = document.getElementById("caishen");
    _this.ctx = container.getContext("2d");
    _this.clientWidth = container.width;
    _this.clientHeight = container.height;
    _this.channelWidth = Math.floor(_this.clientWidth / 5);
    _this.caishenImg = new Image();
    _this.caishenImg.src = require("@/assets/img/caishen/财神爷.png");
 
    _this.initTreasures();
    countDownInit = _this.countDown;
    _this.caishen.x = _this.clientWidth / 2 - 60;
    _this.caishen.y = _this.clientHeight - 120;
    document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if (key === 37) {
        //Left button_this.caishen.dx = -_this.caishen.speed;
      } else if (key === 39) {
        //Right button_this.caishen.dx = _this.caishen.speed;
      }
    };
    document.onkeyup = function(e) {
      _this.caishen.dx = 0;
    };
    _this.caishenImg.onload = function() {
      (function animloop() {
        _this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight);
        _this.loop();
        animationId = window.requestAnimationFrame(animloop);
        if (_this.countDown === 0) {
          _this.gameOver = true;
          window.cancelAnimationFrame(animationId);
        }
        if (Date.now() - _this.timeTag >= 1000) {
          _this.countDown--;
          _this.timeTag = Date.now();
        }
      })();
    };
  },
  methods: {
    initTreasures() {
      let _this = this;
      Object.keys(_this.treasureObj).forEach(key => {
        _this.treasureObj[key].src = new Image();
        _this.treasureObj[
          key
        ].src.src = require(`@/assets/img/caishen/${key}.png`);
      });
      for (let i = 0; i < MaxNum; i++) {
        let random = Math.floor(Math.random() * TreasureNames.length);
        _this.treasureArr.push({
          x: _this.channelWidth * (1 / 2 + i) - 30,
          y: _this.getRandomArbitrary(0, 20),
          name: TreasureNames[random],
          speed: _this.getRandomArbitrary(2, 4)
        });
      }
    },
    loop() {
      let _this = this;
      _this.drawCountDown();
      _this.drawCaishen();
      _this.moveCaishen();
      _this.generateTreasure();
      _this.drawTreasure();
      _this.drawScore();
    },
    drawCaishen() {
      let _this = this;
      _this.ctx.save();
      _this.ctx.drawImage(
        _this.caishenImg,
        _this.caishen.x,
        _this.caishen.y,
        120,
        120
      );
      _this.ctx.restore();
    },
    moveCaishen() {
      this.caishen.x += this.caishen.dx;
      if (this.caishen.x > this.clientWidth - 120) {
        this.caishen.x = this.clientWidth - 120;
      } else if (this.caishen.x < 0) {
        this.caishen.x = 0;
      }
    },
    drawScore() {
      let _this = this;
      _this.ctx.beginPath();
      _this.ctx.fillStyle = "#fff";
      _this.ctx.textAlign = "center";
      _this.ctx.textBaseline = "middle";
      _this.ctx.fillText(_this.score + "分", 30, _this.clientHeight - 10);
      _this.ctx.closePath();
    },
    drawCountDown() {
      // Draw the progress ring let _this = this;
      _this.progress += Date.now() - _this.timeTag2;
      _this.timeTag2 = Date.now();
      _this.ctx.beginPath();
      _this.ctx.moveTo(50, 50);
      _this.ctx.arc(
        50,
        50,
        40,
        Math.PI * 1.5,
        Math.PI * (1.5 + 2 * (_this.progress / (countDownInit * 1000))),
        false
      );
      _this.ctx.closePath();
      _this.ctx.fillStyle = "yellow";
      _this.ctx.fill();
 
      // Draw a filled circle_this.ctx.beginPath();
      _this.ctx.arc(50, 50, 30, 0, Math.PI * 2);
      _this.ctx.closePath();
      _this.ctx.fillStyle = "#fff";
      _this.ctx.fill();
 
      //Fill text_this.ctx.font = "bold 16px Microsoft YaHei";
      _this.ctx.fillStyle = "#333";
      _this.ctx.textAlign = "center";
      _this.ctx.textBaseline = "middle";
      _this.ctx.moveTo(50, 50);
      _this.ctx.fillText(_this.countDown + "s", 50, 50);
    },
    filterTreasure(item) {
      let _this = this;
      if (
        item.x <= _this.caishen.x + 110 &&
        item.x >= _this.caishen.x &&
        item.y > _this.caishen.y
      ) {
        // Determine the touching range with the God of Wealth_this.score += _this.treasureObj[item.name].score;
        return false;
      }
      if (item.y >= _this.clientHeight) {
        return false;
      }
      return true;
    },
    drawTreasure() {
      let _this = this;
      _this.treasureArr = _this.treasureArr.filter(_this.filterTreasure);
      _this.treasureArr.forEach(item => {
        _this.ctx.drawImage(
          _this.treasureObj[item.name].src,
          item.x,
          item.y,
          60,
          60
        );
        item.y += item.speed;
      });
    },
    getRandomArbitrary(min, max) {
      return Math.random() * (max - min) + min;
    },
    generateTreasure() {
      let _this = this;
      if (_this.treasureArr.length < MaxNum) {
        let random = Math.floor(Math.random() * TreasureNames.length);
        let channel = _this.getRandomArbitrary(1, 5);
        _this.treasureArr.push({
          x: _this.channelWidth * (1 / 2 + (channel - 1)) - 30,
          y: 0,
          name: TreasureNames[random],
          speed: _this.getRandomArbitrary(2, 4)
        });
      }
    }
  }
};
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
#caishen
  background-color: #b00600;
  background-image: url("~assets/img/caishen/brick-wall.png");
}
.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>

This is the end of this article about VUE+Canvas realizing the God of Wealth receiving ingots game. For more relevant Vue receiving ingots game content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Vue uses Canvas to generate random sized and non-overlapping circles
  • Vue uses canvas to realize image compression upload
  • How to draw the timeline with vue+canvas
  • VUE+Canvas realizes the whole process of a simple Gobang game
  • How to use VUE and Canvas to implement a Thunder Fighter typing game
  • VUE+Canvas implements the sample code of the desktop pinball brick-breaking game
  • Vue uses the mouse to draw a rectangle on Canvas
  • Vue uses canvas to implement mobile handwritten signature
  • Vue+canvas realizes puzzle game
  • Vue uses canvas handwriting input to recognize Chinese

<<:  Code analysis of user variables in mysql query statements

>>:  Linux CentOS6.9 installation graphic tutorial under VMware

Recommend

MySQL 8.0.15 installation and configuration graphic tutorial

This article records the installation and configu...

How to optimize the slow Like fuzzy query in MySQL

Table of contents 1. Introduction: 2. The first i...

Detailed explanation of Linux netstat command

Table of contents Linux netstat command 1. Detail...

Solution to the problem that the Vue page image does not display

When making a new version of the configuration in...

HTML tags: sub tag and sup tag

Today I will introduce two HTML tags that I don’t...

Implementation of CSS dynamic height transition animation effect

This question originated from a message on Nugget...

Several ways of running in the background of Linux (summary)

1. nohup Run the program in a way that ignores th...

Linux implements the source code of the number guessing game

A simple Linux guessing game source code Game rul...

When modifying a record in MySQL, the update operation field = field + string

In some scenarios, we need to modify our varchar ...

Detailed usage of React.Children

Table of contents 1. React.Children.map 2. React....

How to create a new user in CentOS and enable key login

Table of contents Create a new user Authorize new...

React implements the addition, deletion, modification and query of todolist

Table of contents Take todolist as an example The...