web game---canvas basic graphics

Base

canvas tag

Canvas is a newly launched tag in H5. This provides a canvas on which patterns can be drawn. The performance consumption of making web games in this way is much smaller than that of manipulating DOM.

If you know that you are doing a browser game, it is best to use canvas to make it in order to ensure performance.

Coordinate System

The coordinate system of the canvas is the same as that of the browser, with the upper left corner of the canvas as the origin. The x coordinate increases to the left, and the y coordinate increases to the right. The default unit is pixel.

drawing method

Canvas uses imperative programming, uses statements to describe the drawing path and the properties of the brush, and finally performs coloring and closes the path. The following is the straight line drawing process of the canvas.

// 获取画布
const canvas = document.querySelector('canvas');
// 获取画笔
const ctx = canvas.getContext('2d');
/ 开启路径
ctx.beginPath();
// 移动画笔到某点
ctx.moveTo(0, 0);
// 画直线到某点
ctx.lineTo(800, 600);
// 设置直线的颜色,设置颜色要在上色之前
ctx.strokeStyle = 'lightpink';
// 设置线条宽度,也要在上色之前设置
ctx.lineWidth = 5;
// 上色
ctx.stroke();
// 闭合路径
ctx.closePath();  

drawing function

straight lines and curves

In the above example, we draw a straight line, but we can find that many of the above steps are repeated, and there is no need to write it repeatedly, so we can encapsulate it as a function.

// 可以画实线就可以画虚线,虚线本质上就是多条很短的直线,canvas内部提供了一个绘制虚线的函数,只需要我们将封装的函数进行一些修改
// 使用setLineDash() 函数可以设置一条虚线的长度和间距组合,这是对画笔的设置,会影响到之后的所有线条
// 使用getLineDash() 函数可以获取到当前路径下的虚线参数信息
function drawLine(x1, y1, x2, y2, color, width, dashed) {
    
    
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  if(dashed) ctx.setLineDash(dashed);
  ctx.strokeStyle = color;
  ctx.lineWidth = width;
  ctx.stroke();
  // 取消掉之前的曲线配置,防止影响下一条线
  ctx.setLineDash([0,0])
  ctx.closePath();
}

After calling 4 times in a row, we can draw a rectangular border composed of dotted lines

drawLine(100,100,400,100,'pink',5,[5,10]);
drawLine(400,100,400,400,'purple',5);
drawLine(400,400,100,400,'orangered',5,[3,4]);
drawLine(100,400,100,100,'blue',5,[4,6]);

insert image description here

rectangle

Above we drew a rectangle with four different sides, but if it is to draw an ordinary rectangle, using the above method is too cumbersome, we can use the rectangle API used by canvas to draw.

There are three methods for drawing rectangles:

  • rect()Draw an empty rectangle, which can be stroked and filled separately
  • strokeRect()Draw a hollow rectangle, which cannot be filled and will be automatically stroked
  • fillRect()Draw a solid rectangle, cannot stroke, it will be filled automatically

The following code is a rectangle drawing process

ctx.beginPath();
ctx.rect(100,100,300,300);
// 设置填充颜色
ctx.fillStyle = 'skyblue';
ctx.fill();
// 设置描边颜色
ctx.strokeStyle = 'lightpink';
// 设置线宽
ctx.lineWidth = 5;
ctx.stroke();
ctx.closePath();

circles and arcs

Use the arc function to return a circle or an arc, and receive 6 parameters in total. The specific parameters are as follows:

  • The x and y coordinates of the center of the circle
  • circle radius
  • The start angle and end point angle of the circle (in radians, the three o'clock direction is 0)
  • The drawing direction of the circle (optional, the default is counterclockwise) true means counterclockwise, false means clockwise

Here's an example that draws a semicircle:

    ctx.strokeStyle = 'lightpink';
    ctx.lineWidth = 5;
    ctx.arc(250,250,200,0,pi);
    ctx.stroke();

insert image description here

canvas clear

Pass claerRect()to clear a rectangular area.

Clearing a canvas-sized rectangle clears the entire canvas

claerRect(0,0,width,height)

canvas animation

timer animation

Take a gradually appearing circle as an example to demonstrate how the canvas is animated

    const pi = Math.PI;
    const deg = pi * 2 / 360;
    ctx.strokeStyle = 'lightpink';
    ctx.lineWidth = 5;
    let count = 0;
    const timer = setInterval(() => {
    
    
      count++;
      ctx.beginPath();
      ctx.arc(250,250,200,0,deg * count);
      ctx.stroke();
      ctx.closePath();
      if(count == 360) clearInterval(timer);
    }, 10)

Edge Collision Detection

Now I want to make an animation in which the ball turns when it touches the border. Let's do a simple collision with the border first, and then do a collision between two objects.

The following is the implemented code. There are two implementation methods: state method and speed method. It is recommended to use the speed method, which is consistent with physical kinematics and the code is easier to understand:

	// 状态法通过修改状态的真值来决定运动方向
	// let statusX = true;
    // let statusY = true;
    
	// 速度法通过速度的正负来决定运动方向,这种方法与物理知识更契合
    let speedX = 5;
    let speedY = 5;
    const timer = setInterval(() => {
    
     
      // 清空画布
      ctx.clearRect(0,0,w,h);
      // 修改小球方向
      if(x + r == 500) speedX = -5 // statusX = false;
      if(x - r == 0)   speedX = 5  // statusX = true;
      if(y - r == 0)   speedY = 5  // statusY = true;
      if(y + r == 500) speedY = -5 // statusY = false;

      // 根据小球的移动方向修改小球的坐标
      // if(statusX) {
    
    
      //   x = x + 10;
      // } else {
    
    
      //   x = x - 10;
      // }

      // if(statusY) {
    
    
      //   y = y + 10;
      // } else {
    
    
      //   y = y - 10;
      // }

      x += speedX;
      y += speedY;

      // 绘制小球
      ctx.beginPath();
      ctx.arc(x, y, r, 0, 2*pi);
      ctx.fill();
      ctx.closePath();
    },50)

Using the speed method, you can also use the reverse method to modify the speed, or it is more convenient to set different speeds for the ball

// 设置小球速度
    let speedX = 5;
    let speedY = 8;
// 修改小球方向
    if(x + r >= 500 || x - r <= 0) speedX = -speedX 
    if(y - r <= 0 || y + r >= 500) speedY = -speedY  

object oriented

If you want to make multiple small balls, you need to use the idea of ​​oriented objects, and store each small ball as an object, which can solve the problem that it cannot be obtained because it is a painting.
code show as below:

	class Ball {
    
    
      constructor() {
    
    
        this.r = this.ran(90) + 10;
        // 防止出现边缘性问题
        this.x = this.ran(200) + this.r;
        this.y = this.ran(200) + this.r;
        
        this.color = `#${
      
      parseInt(Math.random() * 0xffffff).toString(16)}`;
        this.speedX = this.ran(5) + 2;
        this.speedY = this.ran(4) + 1;
        this.move();
      }

      ran(number) {
    
    
        return Math.random() * number;
      }

      show() {
    
    
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, pi*2);
        ctx.fillStyle = this.color;
        ctx.fill();
        ctx.closePath();
      }

      move() {
    
    
        if(this.x - this.r <= 0 || this.x + this.r >= w) this.speedX = -this.speedX;
        if(this.y - this.r <= 0 || this.y + this.r >= h) this.speedY = -this.speedY;
        this.x += this.speedX;
        this.y += this.speedY;
        this.show();
      }
    }
    
    // 生成并存储小球
    const ballArr = [];
    for(let i = 0; i < 100000; i++){
    
    
      ballArr.push(new Ball());
    }

    // 小球定时运动
    setInterval(() => {
    
    
      ctx.clearRect(0,0,w,h);
      ballArr.forEach(item => {
    
    
        item.move();
      })
    }, 10)

Guess you like

Origin blog.csdn.net/m0_66711291/article/details/128767179