我正在尝试学习HTML5,并找到了一个非常简单的粒子系统,我稍微修改了一下。 如果粒子之间的距离在0-20范围内,我想在粒子之间创建一条线。
我目前所拥有的在每个粒子之间画一条线,无论距离如何。
这是我尝试检查距离的地方,但我无法弄清楚如何做到这一点。非常感谢任何帮助和解释。提前谢谢。
// This particle
var p = particles[t];
// Check position distance to other particles
for (var q = 0; q < particles.length; q++) {
if (particles[q].x - p.x < line_distance || p.x - particles[q].x < line_distance) {
ctx.beginPath();
ctx.lineWidth = .1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}
}
// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Set fullscreen
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;
// Options
var num =30; // Number of particles to draw
var size = 3; // Particle size
var color = '#fff'; // Particle color
var min_speed = 1; // Particle min speed
var max_speed = 3; // Particle max speed
var line_distance = 20; // This is the max distance between two particles
// if we want to draw a line between them
// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
particles.push(
new create_particle()
);
}
// Lets animate the particle
function draw() {
// Background
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Lets draw particles from the array now
for (var t = 0; t < particles.length; t++) {
// This particle
var p = particles[t];
for (var q = 0; q < particles.length; q++) {
// Check position distance
if (particles[q].x - p.x < line_distance || p.x - particles[q].x < line_distance) {
ctx.beginPath();
ctx.lineWidth = .1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}
}
// Color
ctx.fillStyle = color;
// Circle path
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
ctx.fill();
// Lets use the velocity now
p.x += p.vx;
p.y += p.vy;
// If there is only 1 particle
// show X, Y, and velocity
if (num === 1) {
ctx.fillText('Y:'+ p.y, 20, 20);
ctx.fillText('X:'+ p.x, 20, 40);
ctx.fillText('YV:'+ p.vy, 20, 60);
ctx.fillText('XV:'+ p.vx, 20, 80);
}
// To prevent the balls from moving out of the canvas
if (p.x < size) p.vx*= (p.vx / -p.vx);
if (p.y < size) p.vy*= (p.vy / -p.vy);
if (p.x > canvas.width - size) p.vx*= (-p.vx / p.vx);
if (p.y > canvas.height - size) p.vy*= (-p.vy / p.vy);
}
// Loop
requestAnimationFrame(draw);
}
// Function for particle creation
function create_particle() {
// Random position
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
// Velocity
this.vx = random_int_between(min_speed, max_speed);
this.vy = random_int_between(min_speed, max_speed);
// Color & Size
this.color = color;
this.radius = size;
}
// Random number between (used for speed)
function random_int_between(min, max) {
return Math.floor(Math.random() * max) + min;
}
draw();
&#13;
<canvas id="canvas"></canvas>
&#13;
答案 0 :(得分:3)
由于这是一个N体情况,没有人说过CPU负载。
CPU负载
粒子系统会在处理过载时迅速陷入CPU困境。当您测试每个粒子与另一个粒子时,尤其如此。由于粒子系统几乎总是用于实时图形,因此无效编码会破坏整个动画。
不需要做任何事情
首先,由于您只是在寻找阈值距离,因此只要您知道测试失败,就不会继续计算,从而优化计算。
因此设置阈值距离
var dist = 20;
var distSq = dist * dist; // No need to square this inside loops
然后在循环中计算测试并继续。假设p1和p2是粒子
x = p2.x-p1.x; // do x first
if((x *= x) < distSq){ // does it pass?? if not you have saved calculating y
y = p2.y-p1.y; // now do y as you know x is within distance
if(x + (y * y) < distSq){ // now you know you are within 20
// draw the line
假设只有1/6会通过并且1/3接近,则可以节省超过一半的CPU负载。您还会注意到我不使用距离较远的CPU sqrt。没有必要,因为数字和数字的平方之间存在一对一的匹配。如果数字的平方根小于距离,那么数字的平方将小于距离的平方。
N body Squared
永远不要像这样使用两个for循环做N body sim。
for(i = 0; i < particles.length; i ++){
for(j = 0; j < particles.length; j ++){
// you will test all i for j and all j for i but half of them are identical
// and the square root of the number are self to self
这让我很伤心,因为解决方案非常简单。
假设您每秒60帧有100个粒子,那么对于100个粒子,您每秒进行60 * 100 * 100比较(600,000)。这完全是浪费CPU时间。
永远不要做两次,或者你知道答案。
改进for循环并避免测试你已经知道的距离并测试每个粒子与自身的距离
var len = particles.length; // move the length out as it can be expensive
// and pointless as the value does not change;
for(i = 0; i < len; i ++){
for(j = i + 1; j < len; j ++){
// Now you only test each particle against each other once rather than twice
因此,只需几个简单的字符(for(j = 0
成为for(j = i + 1
)就可以占用CPU负载的一半以上,从600,000比较到低于300,000
人眼很容易傻瓜
愚弄眼睛是从动画中获得额外性能的最佳方式。这是一种视觉效果,人眼看不到像素,也不会看到每秒1/60秒的单个帧,但确实看到帧率下降。创建复杂的粒子系统可以获得出色的效果,但如果降低帧速率,则会失去优势。充分利用这样一个事实:像素很小,1/20秒超出了人类发现错误的能力,这是优化FX并增加每个CPU滴答声的最佳方式。
下面的演示有两个粒子模拟器。每个100分。 49像素内的任何点都有一条线。我做的所有东西都是我在另一个上面展示的东西,牺牲了一点记忆和很多精力,只计算每帧1/3点之间的距离。由于最大速度可以接近一帧的线长度的一半,跳过2帧可以使一条线长两倍或两条点在没有线的情况下太近。这样做可以节省大量的CPU,但你不能选择哪个。
点击您认为跳过点的SIM卡,找出哪个是。
var canvas = document.createElement("canvas");
canvas.width= 540;
canvas.height = 270;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
mouseX = 0;
mouseB = false;
function clickedFun(event){
mouseX = event.clientX
mouseB = true;
}
canvas.addEventListener("click",clickedFun);
var w = 250;
var h = 250;
var wh = w/2;
var hh = h/2;
var speedMax = 5;
var partSize = 2;
var count = 100
var grav = 1;
var pA1 = []; // particle arrays
var pA2 = [];
var PI2 = Math.PI * 2;
// populate particle arrays
for(var i = 0; i < count; i += 1){
// dumb list
pA1.push({
x : Math.random() * w,
y : Math.random() * h,
dx : (Math.random() -0.5)*speedMax,
dy : (Math.random() -0.5)*speedMax,
})
// smart list
pA2.push({
x : Math.random() * w,
y : Math.random() * h,
dx : (Math.random() -0.5)*speedMax,
dy : (Math.random() -0.5)*speedMax,
links : [], // add some memory
})
for(var j = 0; j < count; j += 1){
pA2[i].links[i] = false; // set memory to no links
}
}
// move and draw the dots. Just a simple gravity sim
function drawAll(parts){
var x,y,d;
var i = 0;
var len = parts.length;
var p;
ctx.beginPath();
for(;i < len; i++){
p = parts[i];
x = wh-p.x;
y = hh-p.y;
d = x*x + y*y;
x *= grav / d;
y *= grav / d;
p.dx += x;
p.dy += y;
p.x += p.dx;
p.y += p.dy;
if(p.x <= 0){
p.dx -= p.dx/2;
p.x = 1;
}else
if(p.x >= w){
p.dx -= p.dx/2;
p.x = w-1;
}
if(p.y <= 0){
p.dy -= p.dy/2;
p.y = 1;
}else
if(p.y >= h){
p.dy -= p.dy/2;
p.y = w-1;
}
ctx.moveTo(p.x+partSize,p.y)
ctx.arc(p.x,p.y,partSize,0,PI2)
}
ctx.fill();
}
//Old style line test. If two particles are less than dist apart
// draw a line between them
function linesBetween(parts,dist){
var distSq = dist*dist;
var x,y,d,j;
var i = 0;
var len = parts.length;
var p,p1;
ctx.beginPath();
for(; i < len; i ++){
p = parts[i];
for(j = i + 1; j < len; j ++){
p1 = parts[j];
x = p1.x-p.x;
if((x *= x) < distSq){
y = p1.y-p.y;
if(x + (y*y) < distSq){
ctx.moveTo(p.x,p.y);
ctx.lineTo(p1.x,p1.y)
}
}
}
}
ctx.stroke();
}
var counter = 0;// counter for multyplexing
// Fast version. As the eye can not posible see the differance of
// of 4 pixels over 1/30th of a second only caculate evey third
// particls
function linesBetweenFast(parts,dist){
var distSq = dist*dist;
var x,y,d,j,l;
var i = 0;
counter += 1;
var cc = counter % 3;
var wr,re;
var len = parts.length;
var p,p1;
var lineSet
ctx.beginPath();
for(; i < len; i ++){
p = parts[i];
l = p.links;
for(j = i + 1; j < len; j += 1){
p1 = parts[j];
if((j + cc)%3 === 0){ // only every third particle
lineSet = false; // test for diferance default to fail
x = p1.x-p.x;
if((x *= x) < distSq){
y = p1.y-p.y;
if(x + (y*y) < distSq){
lineSet = true; // yes this needs a line
}
}
l[j] = lineSet; // flag it as needing a line
}
if(l[j]){ // draw the line if needed
ctx.moveTo(p.x,p.y);
ctx.lineTo(p1.x,p1.y);
}
}
}
ctx.stroke();
}
var drawLines; // to hold the function that draws lines
// set where the screens are drawn
var left = 10;
var right = 10 * 2 + w;
// Now to not cheat swap half the time
if(Math.random() < 0.5){
right = 10;
left = 10 * 2 + w;
}
// draws a screem
var doScreen = function(parts){
ctx.fillStyle = "red"
drawAll(parts);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
drawLines(parts,49);
}
var guess = ""
var guessPos;
var gueesCol;
ctx.font = "40px Arial Black";
ctx.textAlign = "center";
ctx.textBasline = "middle"
var timer = 0;
function update(){
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.setTransform(1,0,0,1,left,10);
ctx.strokeStyle = "red";
ctx.lineWidth = 4;
ctx.strokeRect(0,0,w,h);
drawLines = linesBetween;
doScreen(pA1)
ctx.setTransform(1,0,0,1,right,10);
ctx.strokeStyle = "red";
ctx.lineWidth = 4;
ctx.strokeRect(0,0,w,h);
drawLines = linesBetweenFast
doScreen(pA2)
if(mouseB){
if((mouseX > 270 && right >250) ||
(mouseX < 250 && right < 250)){
guess = "CORRECT!"
guessPos = right;
guessCol = "Green";
}else{
guess = "WRONG"
guessPos = left
guessCol = "Red";
}
timer = 120;
mouseB = false;
}else
if(timer > 0){
timer -= 1;
if(timer > 30){
ctx.setTransform(1,0,0,1,guessPos,10);
ctx.font = "40px Arial Black";
ctx.fillStyle = guessCol;
ctx.fillText(guess,w/2,h/2);
}else{
if(Math.random() < 0.5){
right = 10;
left = 10 * 2 + w;
}else{
left = 10;
right = 10 * 2 + w;
}
}
}else{
ctx.setTransform(1,0,0,1,0,0);
ctx.font = "16px Arial Black";
var tw = ctx.measureText("Click which sim skips 2/3rd of").width +30;
ctx.beginPath();
ctx.fillStyle = "#DDD";
ctx.strokeStyle = "Red";
ctx.rect(270-tw/2,-5,tw,40);
ctx.stroke();
ctx.fill();
ctx.fillStyle = "blue";
ctx.fillText("Click which sim skips 2/3rd of",270,15) ;
ctx.fillText("particle tests every frame",270,30) ;
}
requestAnimationFrame(update);
}
update();
答案 1 :(得分:2)
要计算两点之间的距离,你应该使用毕达哥拉斯定理:
length = sqrt(a² + b²)
其中a是一边的长度,b是另一边的长度。
var a = (x2 - x1);
var b = (y2 - y1);
var sum = (a * a) + (b * b);
var length = Math.sqrt(sum);
这可以变成一个函数,因为你知道你的粒子有x和y。
function calcLength(particle1, particle2) {
var xDiff = particle2.x - particle1.x;
var yDiff = particle2.y - particle1.y;
var sum = (xDiff * xDiff) + (yDiff * yDiff);
return Math.sqrt(sum);
}
然后您可以在代码中使用该功能:
for (var t = 0; t < particles.length; t++) {
var p = particles[t];
for (var q = 0; q < particles.length; q++) {
var p2 = particles[q];
if (calcLength(p, p2) < 20) {
// draw a line between the particles
}
}
}
答案 2 :(得分:2)
这只是你的测试,这是错误的。
a-b&lt; c || b-a&lt; c始终为真(除非a-b == c)
替换为abs(a-b)&lt; c如果你想测试“x”距离,或者如果你想要一个欧几里德距离,可以使用上面的公式
// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Set fullscreen
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;
// Options
var num =30; // Number of particles to draw
var size = 3; // Particle size
var color = '#fff'; // Particle color
var min_speed = 1; // Particle min speed
var max_speed = 3; // Particle max speed
var line_distance = 20; // This is the max distance between two particles
// if we want to draw a line between them
// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
particles.push(
new create_particle()
);
}
// Lets animate the particle
function draw() {
// Background
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Lets draw particles from the array now
for (var t = 0; t < particles.length; t++) {
// This particle
var p = particles[t];
for (var q = 0; q < particles.length; q++) {
// Check position distance
if (Math.abs(particles[q].x - p.x) < line_distance) {
ctx.beginPath();
ctx.lineWidth = .1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}
}
// Color
ctx.fillStyle = color;
// Circle path
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
ctx.fill();
// Lets use the velocity now
p.x += p.vx;
p.y += p.vy;
// If there is only 1 particle
// show X, Y, and velocity
if (num === 1) {
ctx.fillText('Y:'+ p.y, 20, 20);
ctx.fillText('X:'+ p.x, 20, 40);
ctx.fillText('YV:'+ p.vy, 20, 60);
ctx.fillText('XV:'+ p.vx, 20, 80);
}
// To prevent the balls from moving out of the canvas
if (p.x < size) p.vx*= (p.vx / -p.vx);
if (p.y < size) p.vy*= (p.vy / -p.vy);
if (p.x > canvas.width - size) p.vx*= (-p.vx / p.vx);
if (p.y > canvas.height - size) p.vy*= (-p.vy / p.vy);
}
// Loop
requestAnimationFrame(draw);
}
// Function for particle creation
function create_particle() {
// Random position
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
// Velocity
this.vx = random_int_between(min_speed, max_speed);
this.vy = random_int_between(min_speed, max_speed);
// Color & Size
this.color = color;
this.radius = size;
}
// Random number between (used for speed)
function random_int_between(min, max) {
return Math.floor(Math.random() * (max-min)) + min;
}
draw();
<canvas id="canvas" width="300" height="300"></canvas>
// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Set fullscreen
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;
// Options
var num =30; // Number of particles to draw
var size = 3; // Particle size
var color = '#fff'; // Particle color
var min_speed = 1; // Particle min speed
var max_speed = 3; // Particle max speed
var line_distance = 20; // This is the max distance between two particles
// if we want to draw a line between them
// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
particles.push(
new create_particle()
);
}
// Lets animate the particle
function draw() {
// Background
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Lets draw particles from the array now
for (var t = 0; t < particles.length; t++) {
// This particle
var p = particles[t];
for (var q = 0; q < particles.length; q++) {
// Check position distance
if (particles[q].x - p.x < line_distance || p.x - particles[q].x < line_distance) {
ctx.beginPath();
ctx.lineWidth = .1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}
}
// Color
ctx.fillStyle = color;
// Circle path
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
ctx.fill();
// Lets use the velocity now
p.x += p.vx;
p.y += p.vy;
// If there is only 1 particle
// show X, Y, and velocity
if (num === 1) {
ctx.fillText('Y:'+ p.y, 20, 20);
ctx.fillText('X:'+ p.x, 20, 40);
ctx.fillText('YV:'+ p.vy, 20, 60);
ctx.fillText('XV:'+ p.vx, 20, 80);
}
// To prevent the balls from moving out of the canvas
if (p.x < size) p.vx*= (p.vx / -p.vx);
if (p.y < size) p.vy*= (p.vy / -p.vy);
if (p.x > canvas.width - size) p.vx*= (-p.vx / p.vx);
if (p.y > canvas.height - size) p.vy*= (-p.vy / p.vy);
}
// Loop
requestAnimationFrame(draw);
}
// Function for particle creation
function create_particle() {
// Random position
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
// Velocity
this.vx = random_int_between(min_speed, max_speed);
this.vy = random_int_between(min_speed, max_speed);
// Color & Size
this.color = color;
this.radius = size;
}
// Random number between (used for speed)
function random_int_between(min, max) {
return Math.floor(Math.random() * max) + min;
}
draw();
<canvas id="canvas"></canvas>
答案 3 :(得分:1)
要计算两点之间的距离,请使用毕达哥拉斯定理。 http://www.purplemath.com/modules/distform.htm
// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Set fullscreen
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;
// Options
var num =30; // Number of particles to draw
var size = 3; // Particle size
var color = '#fff'; // Particle color
var min_speed = 1; // Particle min speed
var max_speed = 3; // Particle max speed
var line_distance = 20; // This is the max distance between two particles
// if we want to draw a line between them
// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
particles.push(
new create_particle()
);
}
// Lets animate the particle
function draw() {
// Background
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Lets draw particles from the array now
for (var t = 0; t < particles.length; t++) {
// This particle
var p = particles[t];
for (var q = 0; q < particles.length; q++) {
// Check position distance
if (distance(particles[q], p) < line_distance) {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}
}
// Color
ctx.fillStyle = color;
// Circle path
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
ctx.fill();
// Lets use the velocity now
p.x += p.vx;
p.y += p.vy;
// If there is only 1 particle
// show X, Y, and velocity
if (num === 1) {
ctx.fillText('Y:'+ p.y, 20, 20);
ctx.fillText('X:'+ p.x, 20, 40);
ctx.fillText('YV:'+ p.vy, 20, 60);
ctx.fillText('XV:'+ p.vx, 20, 80);
}
// To prevent the balls from moving out of the canvas
if (p.x < size) p.vx*= (p.vx / -p.vx);
if (p.y < size) p.vy*= (p.vy / -p.vy);
if (p.x > canvas.width - size) p.vx*= (-p.vx / p.vx);
if (p.y > canvas.height - size) p.vy*= (-p.vy / p.vy);
}
// Loop
requestAnimationFrame(draw);
}
// Function for particle creation
function create_particle() {
// Random position
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
// Velocity
this.vx = random_int_between(min_speed, max_speed);
this.vy = random_int_between(min_speed, max_speed);
// Color & Size
this.color = color;
this.radius = size;
}
// Random number between (used for speed)
function random_int_between(min, max) {
return Math.floor(Math.random() * max) + min;
}
draw();
function distance(pointA, pointB){
var dx = pointB.x - pointA.x;
var dy = pointB.y - pointA.y;
return Math.sqrt(dx*dx + dy*dy);
}
&#13;
<canvas id="canvas"></canvas>
&#13;
请注意我将lineWidth增加到1,因此您可以更好地看到结果
答案 4 :(得分:1)
您有一个坐标系 - 使用Pythagorean theorem。