Cod16曳光弹:球互相弹开(bouncing off)

我的问题是,如何使它们在击中时彼此反弹,以及在击中时从矩形反弹?

var mycanvas =document.getElementById("mycanvas");
var ctx=mycanvas.getContext("2d");
var w=500,h=500;
mycanvas.height=h;
mycanvas.width=w;
var ball=[];
function Ball(x,y,r,c,vx,vy){
  this.x=x; //starting x coordinate
  this.y=y; //starting x coordinate
  this.r=r; //radius  
  this.c=c; //color 
  this.vx=vx; // x direction speed
  this.vy=vy; // y direction speed
  this.update=function(){
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, Math.PI*2, false);
        ctx.fillStyle = this.c;
        ctx.fill();
        ctx.closePath();
        this.x += this.vx;
        this.y += this.vy;
        //changing direction on hitting wall
        if(this.y>=(w-10)||this.y<=10){
        this.vy=-this.vy;
         }
        if(this.x>=(h-10)||this.x<=10){
        this.vx=-this.vx;
         }
}
}
function clearCanvas(){
ctx.clearRect(0, 0, w, h);
}
var count;
for (count = 0; count < 20; count++) {
  var rndColor=Math.floor((Math.random() * 9) + 1); //random color
    ball[count]= new Ball(Math.floor((Math.random() * 490) + 1),Math.floor((Math.random() * 490)+1),5,'red',5,5);
}
function update(){
  var i;
  clearCanvas();
    //draw rectangle 
    ctx.rect(250, 200, 10, 100);
    ctx.fillStyle = 'yellow';
    ctx.fill();
  for(i=0;i<count;i++){
        ball[i].update();
    }
}
setInterval(update, 1000/60);
2

要互相弹开球,他是你需要知道的

球碰撞了吗?确定的方法是测量两个圆心之间的距离,如果小于组合半径,则球碰撞了

碰撞后它们应该有什么方向?使用使用 atan2 来计算两个球的中心之间的角度。然后将它们设置在该角度的相反方向上,以使它们不会彼此更深。当然,这个简单的解决方案忽略了球可能具有的现有动量。但是进行动量计算(涉及质量,速度和当前角度)更加复杂。

2

您可以使用几种方法。以下方法是最简单的方法。

使现代化

我添加了一个使用第二种方法的示例。请参阅底部的代码段。

定义球

每个示例都是一个名为Ball的对象。

// x,y position of center, 
// vx,vy is velocity, 
// r is radius defaults 45, 
// m is m defaults to the volume of the sphere of radius r
function Ball(x, y, vx, vy, r = 45, m = (4 / 3 * Math.PI * (r ** 3)) {
    this.r = r;
    this.m = m;
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
}
Ball.prototype = {
    // add collision functions here
};

代码假设球接触。

弹性碰撞

使用的逻辑可以在wikis elastic collision页找到

计算将每个球的力分成两部分。(2 个球共 4 个)

能量沿着球之间的线传递,

沿碰撞点切线调整每个球的能量

等质量

每个球具有相同的质量,这意味着能量的传递是平衡的,可以忽略

函数被调用后,每个球都有一个新的速度矢量。

请注意,如果你调用碰撞和速度意味着球彼此远离(碰撞悖论),那么结果将有球移动到彼此(分辨率悖论)

为了使数学保持简单,将矢量大小u1u2u3u4转换为一个单位,该单位是球中心之间的线的长度(d的平方根)

collide(b) {  // b is the ball that the collision is with
    const a = this;
    const x = a.x - b.x;
    const y = a.y - b.y;  
    const d = x * x + y * y;
    const u1 = (a.vx * x + a.vy * y) / d;  // From this to b
    const u2 = (x * a.vy - y * a.vx) / d;  // Adjust self along tangent
    const u3 = (b.vx * x + b.vy * y) / d;  // From b to this
    const u4 = (x * b.vy - y * b.vx) / d;  // Adjust b  along tangent
    // set new velocities
    b.vx = x * u1 - y * u4;
    b.vy = y * u1 + x * u4;
    a.vx = x * u3 - y * u2;
    a.vy = y * u3 + x * u2;     
},

不同群众

每个球都有自己的质量,因此传输需要计算与传输质量相关的能量。

只有沿着球之间的线传递的能量受质量差异的影响

collideM(b) {
    const a = this;
    const m1 = a.m;
    const m2 = b.m;        
    const x = a.x - b.x;
    const y = a.y - b.y;
    const d = x * x + y * y;
    const u1 = (a.vx * x + a.vy * y) / d; 
    const u2 = (x * a.vy - y * a.vx) / d;  
    const u3 = (b.vx * x + b.vy * y) / d;    
    const u4 = (x * b.vy - y * b.vx) / d; 
    const mm = m1 + m2;
    const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
    const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
    b.vx = x * vu1 - y * u4;
    b.vy = y * vu1 + x * u4;
    a.vx = x * vu3 - y * u2;
    a.vy = y * vu3 + x * u2;     
},

实例

简单的球碰撞示例。由线绑定的球(注意线有外部和内部,如果从开始到结束看,内部在右侧)

冲突按帧之间的时间顺序完全解决。使用的时间是一个帧,其中 0 是前一帧,1 是当前帧。

canvas.width = innerWidth -20;
canvas.height = innerHeight -20;
const ctx = canvas.getContext("2d");
const GRITY = 0;
const WALL_LOSS = 1;
const BALL_COUNT = 20;  // approx as will not add ball if space can not be found
const MIN_BALL_SIZE = 13;
const MAX_BALL_SIZE = 20;
const VEL_MIN = 1;
const VEL_MAX = 5; 
const MAX_RESOLUTION_CYCLES = 100;
Math.TAU = Math.PI * 2;
Math.rand = (min, max) => Math.random() * (max - min) + min;
Math.randI = (min, max) => Math.random() * (max - min) + min | 0; // only for positive numbers 32bit signed int
Math.randItem = arr => arr[Math.random() * arr.length | 0]; // only for arrays with length < 2 ** 31 - 1
// contact points of two circles radius r1, r2 moving along two lines (a,e)-(b,f) and (c,g)-(d,h) [where (,) is coord (x,y)]
Math.circlesInterceptUnitTime = (a, e, b, f, c, g, d, h, r1, r2) => { // args (x1, y1, x2, y2, x3, y3, x4, y4, r1, r2)
    const A = a * a, B = b * b, C = c * c, D = d * d;
    const E = e * e, F = f * f, G = g * g, H = h * h;
    var R = (r1 + r2) ** 2;
    const AA = A + B + C + F + G + H + D + E + b * c + c * b + f * g + g * f + 2 * (a * d - a * b - a * c - b * d - c * d - e * f + e * h - e * g - f * h - g * h);
    const BB = 2 * (-A + a * b + 2 * a * c - a * d - c * b - C + c * d - E + e * f + 2 * e * g - e * h - g * f - G + g * h);
    const CC = A - 2 * a * c + C + E - 2 * e * g + G - R;
    return Math.quadRoots(AA, BB, CC);
}   
Math.quadRoots = (a, b, c) => { // find roots for quadratic
    if (Math.abs(a) < 1e-6) { return b != 0 ? [-c / b] : []  }
    b /= a;
    var d = b * b - 4 * (c / a);
    if (d > 0) {
        d = d ** 0.5;
        return  [0.5 * (-b + d), 0.5 * (-b - d)]
    }
    return d === 0 ? [0.5 * -b] : [];
}
Math.interceptLineBallTime = (x, y, vx, vy, x1, y1, x2, y2,  r) => {
    const xx = x2 - x1;
    const yy = y2 - y1;
    const d = vx * yy - vy * xx;
    if (d > 0) {  // only if moving towards the line
        const dd = r / (xx * xx + yy * yy) ** 0.5;
        const nx = xx * dd;
        const ny = yy * dd;
        return (xx * (y - (y1 + nx)) - yy * (x -(x1 - ny))) / d;
    }
}
const  = [];
const lines = [];
function Line(x1,y1,x2,y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}
Line.prototype = {
    draw() {
        ctx.moveTo(this.x1, this.y1);
        ctx.lineTo(this.x2, this.y2);
    },
    reverse() {
        const x = this.x1;
        const y = this.y1;
        this.x1 = this.x2;
        this.y1 = this.y2;
        this.x2 = x;
        this.y2 = y;
        return this;
    }
}
    
function Ball(x, y, vx, vy, r = 45, m = 4 / 3 * Math.PI * (r ** 3)) {
    this.r = r;
    this.m = m
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
}
Ball.prototype = {
    update() {
        this.x += this.vx;
        this.y += this.vy;
        this.vy += GRITY;
    },
    draw() {
        ctx.moveTo(this.x + this.r, this.y);
        ctx.arc(this.x, this.y, this.r, 0, Math.TAU);
    },
    interceptLineTime(l, time) {
        const u = Math.interceptLineBallTime(this.x, this.y, this.vx, this.vy, l.x1, l.y1, l.x2, l.y2,  this.r);
        if(u >= time && u <= 1) {
            return u;
        }
    },
    checkBallBallTime(t, minTime) {
        return t > minTime && t <= 1;
    },
    interceptBallTime(b, time) {
        const x = this.x - b.x;
        const y = this.y - b.y;
        const d = (x * x + y * y) ** 0.5;
        if(d > this.r + b.r) {
            const times = Math.circlesInterceptUnitTime(
                this.x, this.y, 
                this.x + this.vx, this.y + this.vy, 
                b.x, b.y,
                b.x + b.vx, b.y + b.vy, 
                this.r, b.r
            );
            if(times.length) {
                if(times.length === 1) {
                    if(this.checkBallBallTime(times[0], time)) { return times[0] }
                    return;
                }
                if(times[0] <= times[1]) {
                    if(this.checkBallBallTime(times[0], time)) { return times[0] }
                    if(this.checkBallBallTime(times[1], time)) { return times[1] }
                    return
                }
                if(this.checkBallBallTime(times[1], time)) { return times[1] }                
                if(this.checkBallBallTime(times[0], time)) { return times[0] }
            }
        }
    },
    collideLine(l, time) {
        const x1 = l.x2 - l.x1;
        const y1 = l.y2 - l.y1;
        const d = (x1 * x1 + y1 * y1) ** 0.5;
        const nx = x1 / d;
        const ny = y1 / d;            
        const u = (this.vx  * nx + this.vy  * ny) * 2;
        this.x += this.vx * time;   
        this.y += this.vy * time;   
        this.vx = (nx * u - this.vx) * WALL_LOSS;
        this.vy = (ny * u - this.vy) * WALL_LOSS;
        this.x -= this.vx * time;
        this.y -= this.vy * time;
    },
    collide(b, time) {
        const a = this;
        const m1 = a.m;
        const m2 = b.m;
        const x = a.x - b.x
        const y = a.y - b.y  
        const d = (x * x + y * y);
        const u1 = (a.vx * x + a.vy * y) / d
        const u2 = (x * a.vy - y * a.vx ) / d
        const u3 = (b.vx * x + b.vy * y) / d
        const u4 = (x * b.vy - y * b.vx ) / d
        const mm = m1 + m2;
        const vu3 = (m1 - m2) / mm * u1 + (2 * m2) / mm * u3;
        const vu1 = (m2 - m1) / mm * u3 + (2 * m1) / mm * u1;
        a.x = a.x + a.vx * time;
        a.y = a.y + a.vy * time;
        b.x = b.x + b.vx * time;
        b.y = b.y + b.vy * time;
        b.vx = x * vu1 - y * u4;
        b.vy = y * vu1 + x * u4;
        a.vx = x * vu3 - y * u2;
        a.vy = y * vu3 + x * u2;
        a.x = a.x - a.vx * time;
        a.y = a.y - a.vy * time;
        b.x = b.x - b.vx * time;
        b.y = b.y - b.vy * time;
    },
    doesOverlap(ball) {
        const x = this.x - ball.x;
        const y = this.y - ball.y;
        return  (this.r + ball.r) > ((x * x + y * y) ** 0.5);  
    }       
}
function canAdd(ball) {
    for(const b of ) {
        if (ball.doesOverlap(b)) { return false }
    }
    return true;
}
function create(bCount) {
    lines.push(new Line(-10, 10, ctx.canvas.width + 10, 5));
    lines.push((new Line(-10, ctx.canvas.height - 2, ctx.canvas.width + 10, ctx.canvas.height - 10)).reverse());
    lines.push((new Line(10, -10, 4, ctx.canvas.height + 10)).reverse());
    lines.push(new Line(ctx.canvas.width - 3, -10, ctx.canvas.width - 10, ctx.canvas.height + 10)); 
    while (bCount--) {
        let tries = 100;
        de
        while (tries--) {
            const dir = Math.rand(0, Math.TAU);
            const vel = Math.rand(VEL_MIN, VEL_MAX);
            const ball = new Ball(
                Math.rand(MAX_BALL_SIZE + 10, canvas.width - MAX_BALL_SIZE - 10), 
                Math.rand(MAX_BALL_SIZE + 10, canvas.height - MAX_BALL_SIZE - 10),
                Math.cos(dir) * vel,
                Math.sin(dir) * vel,
                Math.rand(MIN_BALL_SIZE, MAX_BALL_SIZE),
            );
            if (canAdd(ball)) {
                .push(ball);
                break;
            }
        }
    }
}
function resolveCollisions() {
    var minTime = 0, minObj, minBall, resolving = true, idx = 0, idx1, after = 0, e = 0;
    while(resolving && e++ < MAX_RESOLUTION_CYCLES) { // too main ball may create very lone resolution cycle. e limits this
        resolving = false;
        minObj = undefined;
        minBall = undefined;
        minTime = 1;
        idx = 0;
        for(const b of ) {
            idx1 = idx + 1;
            while(idx1 < .length) {
                const b1 = [idx1++];
                const time = b.interceptBallTime(b1, after);
                if(time !== undefined) {
                    if(time <= minTime) {
                        minTime = time;
                        minObj = b1;
                        minBall = b;
                        resolving = true;
                    }
                }
            }
            for(const l of lines) {
                const time = b.interceptLineTime(l, after);
                if(time !== undefined) {
                    if(time <= minTime) {
                        minTime = time;
                        minObj = l;
                        minBall = b;
                        resolving = true;
                    }
                }
            }
            idx ++;
        }
        if(resolving) {
            if (minObj instanceof Ball) {
                minBall.collide(minObj, minTime);
            } else {
                minBall.collideLine(minObj, minTime);
            }
            after = minTime;
        }
    }
}
create(BALL_COUNT);
mainLoop();
function mainLoop() {
    ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
    resolveCollisions();
    for(const b of ) { b.update() }
    ctx.strokeStyle = "#000";
    ctx.beginPath();
    for(const b of ) { b.draw() }
    for(const l of lines) { l.draw() }
    ctx.stroke();
    requestAnimationFrame(mainLoop);
}
<canvas id="canvas"></canvas>

本站系公益性非盈利分享网址,本文来自用户投稿,不代表边看边学立场,如若转载,请注明出处

(586)
如何关闭cpu风扇:如何使用OpenHardwareMonitorlib获取cpu风扇速度
上一篇
Set w:如何让PicoW在启动时设置正确的时间
下一篇

相关推荐

  • code vein噬血代码挑战血腥的噬血之旅

    Code Vein噬血代码是一种用于游戏中的特殊能力,它可以让玩家在游戏中拥有更强大的能力。噬血代码可以使玩家获得更多的技能点数,更多的体力,更多的攻击力,更多的防御力,以及更多的技能。…

    2023-03-20 13:41:45
    0 56 23
  • coupang wing下载:购买Coupang Wing,享受无穷的便利和购物乐趣!

    Coupang Wing是一款由韩国电子商务公司Coupang开发的移动应用程序,可以帮助用户购买和支付商品。它提供了一个安全、快捷的购物体验,并允许用户在几分钟内完成购买流程。…

    2023-01-11 15:23:13
    0 23 54
  • wincc程序:设备A发生运行异常,请及时处理。

    示例示例WINCC是一款由西门子公司开发的工业控制系统(SCADA)软件,它可以帮助用户实现远程监控、数据采集、报警和控制等功能。WINCC程序可以通过使用结构化文本语言(STL)、脚本语言(VBS)和C/C++等编程语言来编写,也可以使用现成的页面模板和功能模块来快速实现功能。…

    2023-01-20 03:36:16
    0 85 65
  • win10电脑清理c盘垃圾:如何快速清理Win10电脑C盘垃圾?

    使用磁盘清理工具:Windows 10自带了一个磁盘清理工具,可以清理C盘垃圾。可以通过以下步骤操作:…

    2023-02-22 04:13:36
    0 59 27
  • winsock网络编程经络:如何使用Winsock实现跨平台网络编程

    Winsock(Windows Socket)是一个用于在Windows操作系统中实现网络编程的应用程序编程接口(API)。它使用TCP/IP协议簇,可以在Windows平台上实现网络通信。它允许开发人员创建客户端/服务器应用程序,以实现网络通信。…

    2023-03-15 15:59:45
    0 27 21
  • 外星人checkingmedia解决:外星人入侵游戏中的外星人不动

    关于外星人checkingmedia解决的问题,在alien invasion arcade中经常遇到,我正在尝试学习 python,现在正在进行外星人入侵游戏。我在使外星人向右移动的地方,由于某种原因,外星人舰队不会移动。每当我让它移动时,要么只有边缘在与外星人一起移动,要么整个舰队以奇怪的方式移动(附图)image1…

    2022-11-23 08:43:53
    0 35 10
  • canvas 官网Bring Your Ideas to Life with Creative Artwork

    Canvas 官网是一个用于创建图形的 HTML5 API,它可以在浏览器中使用 JavaScript 来绘制 2D 图形。它提供了一个可以在网页上绘制图形的强大工具,可以用来创建动画、游戏、数据可视化等。…

    2023-02-28 09:52:08
    0 40 67
  • java实现tcp:使用Java实现TCP网络编程

    TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。它使用三次握手来建立可靠的连接,并且在数据传输期间可以检测丢失的数据包并重新发送。…

    2023-01-31 10:33:14
    0 57 16

发表评论

登录 后才能评论

评论列表(86条)