【30分钟学完】canvas动画|游戏基本(6):坐标扭转探讨

媒介

本篇主要讲坐标扭转及其运用,这是编程动画必不可少的手艺。
浏览本篇前请先打好前面的基本。
本人才能有限,迎接牛人配合议论,批评指正。

坐标扭转

模仿场景:已知一个中间点(centerX,centerY),扭转前物体ball(x1,y1),扭转弧度(rotation);求扭转后物体(x2,y2)。(以下图)

《【30分钟学完】canvas动画|游戏基本(6):坐标扭转探讨》

坐标扭转就是说缭绕某个点扭转坐标,我们要根据扭转的角度(弧度),盘算出物体扭转前后的坐标,平常有两种要领:

简朴坐标扭转

灵活运用前章节的三角函数学问能够很轻易处置惩罚,基本思路:

  1. 盘算物体初始相关于中间点的位置;
  2. 运用atan2盘算弧度angle;
  3. 运用勾股定理盘算半径radius;
  4. angle+rotation后运用cos盘算扭转后x轴位置,用sin盘算扭转后y轴位置。

下面是示例是采纳这类要领的圆周运动,个中vr为ball相关于中间点的弧度变化速率,因为扭转半径是牢固的,所以没有在动画轮回里每次都猎取。
完全示例:简朴坐标扭转演示

/**
 * 简朴坐标扭转演示
 * */
window.onload = function () {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const ball = new Ball();
  ball.x = 300;
  ball.y = 200;
  // 弧度变化速率
  const vr = 0.05;
  // 中间点位置设定在画布中间
  const centerX = canvas.width / 2;
  const centerY = canvas.height / 2;
  // ball相对与中间点的间隔
  const dx = ball.x - centerX;
  const dy = ball.y - centerY;
  // ball相对与中间点的弧度
  let angle = Math.atan2(dy, dx);
  // 扭转半径
  const radius = Math.sqrt(dx ** 2 + dy ** 2);

  (function drawFrame() {
    window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);

    ball.x = centerX + Math.cos(angle) * radius;
    ball.y = centerY + Math.sin(angle) * radius;
    angle += vr;
    ball.draw(context);
  }());
};

坐标扭转公式

上面的要领关于单个物体来说是很适宜的,特别是角度和半径只需盘算一次的状况。但是在更动态的场景中,能够须要扭转多个物体,而他们相关于中间点的位置各不相同。所以每一帧都要盘算每一个物体的间隔、角度和半径,然后把vr累加在角度上,末了盘算物体新的坐标。如许明显不会是文雅的做法。
抱负的做法是用数学要领推导出扭转角度与位置的关联,直接每次代入盘算即可。推导历程以下图:

《【30分钟学完】canvas动画|游戏基本(6):坐标扭转探讨》

实在推导历程不重要,我们只须要记着以下两组公式,个中dx2和dy2是ball完毕点相关于中间点的间隔,所以获得物体完毕点,还要离别加上中间点坐标。

// 正向挑选
dx2 = (x1 - centerX) * cos(rotation) - (y1 - centerY) * sin(rotation)
dy2 = (y1 - centerY) * cos(rotation) + (x1 - centerX) * sin(rotation)
// 反向挑选
dx2 = (x1 - centerX) * cos(rotation) + (y1 - centerY) * sin(rotation)
dy2 = (y1 - centerY) * cos(rotation) - (x1 - centerX) * sin(rotation)

下面是示例是采纳这类要领的圆周运动,个中dx1和dy1是ball起始点相关于中间点的间隔,dx2和dy2是ball完毕点相关于中间点的间隔。
完全示例:高等坐标扭转演示

/**
 * 高等坐标扭转演示
 * */
window.onload = function () {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const ball = new Ball();
  ball.x = 300;
  ball.y = 200;
  // 弧度变化速率
  const vr = 0.05;
  // 中间点位置设定在画布中间
  const centerX = canvas.width / 2;
  const centerY = canvas.height / 2;
  // 因为vr是牢固的能够先盘算正弦和余弦
  const cos = Math.cos(vr);
  const sin = Math.sin(vr);

  (function drawFrame() {
    window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);

    // ball相对与中间点的间隔
    const dx1 = ball.x - centerX;
    const dy1 = ball.y - centerY;
    // 代入公式求出ball在完毕相对与中间点的间隔
    const dx2 = dx1 * cos - dy1 * sin;
    const dy2 = dy1 * cos + dx1 * sin;
    // 求出x2,y2
    ball.x = centerX + dx2;
    ball.y = centerY + dy2;
    ball.draw(context);
  }());
};

斜面反弹

前面的章节中我们引见过越界的一种处置惩罚方法是反弹,因为边境是矩形,反弹面垂直或程度,所以能够直接将对应轴的速率取反即可,但关于非垂直或程度的反弹面这类要领是不实用的。
坐标扭转罕见的运用就是处置惩罚这类状况,将不规律方向的复杂问题简朴化。
基本思路:(扭转前后如图)

  1. 运用扭转公式,扭转全部体系,将斜面场景转变为程度场景;
  2. 在程度场景中处置惩罚反弹;
  3. 再扭转返来。

《【30分钟学完】canvas动画|游戏基本(6):坐标扭转探讨》

示例是一个球掉落到一条线上,球遭到重力加速率影响着落,遇到斜面就会反弹,每次反弹都邑消耗速率。
完全示例:斜面反弹示例

window.onload = function () {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const ball = new Ball();
  // line类组织函数参数(开始点x轴坐标,开始点y轴坐标,完毕点x轴坐标,完毕点y轴坐标)
  const line = new Line(0, 0, 500, 0);
  // 设置重力加速率
  const gravity = 0.2;
  // 设置反弹系数
  const bounce = -0.6;

  ball.x = 100;
  ball.y = 100;

  line.x = 0;
  line.y = 200;
  line.rotation = 10 * Math.PI / 180;

  const cos = Math.cos(line.rotation);
  const sin = Math.sin(line.rotation);

  (function drawFrame() {
    window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);

    ball.vy += gravity;
    ball.x += ball.vx;
    ball.y += ball.vy;

    // 猎取ball与line的相对位置
    let x1 = ball.x - line.x;
    let y1 = ball.y - line.y;
    // 扭转坐标系(反向)
    let y2 = y1 * cos - x1 * sin;

    // 根据扭转值实行反弹
    if (y2 > -ball.radius) {
      // 扭转坐标系(反向)
      const x2 = x1 * cos + y1 * sin;
      // 扭转速率(反向)
      const vx1 = ball.vx * cos + ball.vy * sin;
      let vy1 = ball.vy * cos - ball.vx * sin;

      y2 = -ball.radius;
      vy1 *= bounce;

      // 将一切东西反转(正向)
      x1 = x2 * cos - y2 * sin;
      y1 = y2 * cos + x2 * sin;
      ball.vx = vx1 * cos - vy1 * sin;
      ball.vy = vy1 * cos + vx1 * sin;
      ball.x = line.x + x1;
      ball.y = line.y + y1;
    }

    ball.draw(context);
    line.draw(context);
  }());
};
    原文作者:calimanco
    原文地址: https://segmentfault.com/a/1190000014113090
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞