Согласно рекомендациям @Stranger in the Q, я сделал проверку на столкновения отдельным таймером, но так еще хуже. Может есть еще способы получше проверять столкновения?
Проверка во время отрисовки:
function dotInObj(dotX, dotY, objX, objY, objWidth, objHeight) {
if ((dotX >= objX && dotY >= objY) && (dotX <= objX + objWidth && dotY <= objY + objHeight)) return true;
else return false;
} //функция проверки "точка в объекте"
function MacroCollision(obj1, obj2) {
var XColl = false;
var YColl = false;
if ((obj1.X + obj1.width >= obj2.X) && (obj1.X <= obj2.X + obj2.width)) XColl = true;
if ((obj1.Y + obj1.height >= obj2.Y) && (obj1.Y <= obj2.Y + obj2.height)) YColl = true;
if (XColl & YColl) {
return true;
}
return false;
}
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var touch = 0;
var objects = [];
var wall = {
X: 0,
Y: 0,
mass: Infinity,
width: 5,
height: cvs.height,
velocity: 0
}
function createObj(mass, velocity, position) { //функция создания объекта, position[0-1]
objects.push({
X: cvs.width * position - 30,
Y: cvs.height - 30,
mass: mass,
width: 30,
height: 30,
velocity: velocity
});
}
createObj(1000, -100, 1);
createObj(1, 0, 0.5); //созаю объекты
ctx.fillStyle = "#000";
let prevTime = 0;
function draw(t) { //функция отрисовки
let dt = (t - prevTime) / 1000;
ctx.clearRect(0, 0, cvs.width, cvs.height); //очистка после предыдущего кадра
ctx.fillRect(wall.X, wall.Y, wall.width, wall.height); //стена
for (let i = 0; i < objects.length; i++) { //отрисовка объектов
ctx.fillRect(objects[i].X, objects[i].Y, objects[i].width, objects[i].height); //заполнение объекта(ов)
objects[i].X += objects[i].velocity * dt; //передвижение объекта(ов) согласно его(их) скорости
}
let j = 1;
for (let i = 0; i < objects.length; i++) {
if (MacroCollision(objects[i], wall)) { //проверка на стык со стеной
objects[i].velocity = Math.abs(objects[i].velocity) * Math.sign(-objects[i].velocity);
touch++;
}
if (MacroCollision(objects[i], objects[j])) { //проверка столкновения 2 объектов
if (j == i) continue; //от бага
let x1 = objects[j].X + objects[j].width;
let x2 = objects[i].X;
let dx = x1 - x2;
if (dx > 0)
dx *= -1;
objects[j].X += dx;
let tx1 = objects[i].velocity;
let tx2 = objects[j].velocity;
let m2 = objects[i].mass + objects[j].mass;
objects[i].velocity = ((objects[i].mass - objects[j].mass) * tx1 + 2.0 *
objects[j].mass * tx2) / m2;
objects[j].velocity = (2.0 * objects[i].mass * tx1 +
(objects[j].mass - objects[i].mass) * tx2) / m2;
touch++;
}
}
ctx.font = "16px Arial";
ctx.fillText(touch, 10, 20);
requestAnimationFrame(draw);
prevTime = t;
}
/* var timer = setInterval(function() {
}, 1);
*/
requestAnimationFrame(draw);
canvas {
border: 1px solid #999;
}
<div class="canvasBG"></div>
<canvas id="canvas" width="500" height="250"></canvas>
<input type="text" id="console">
Проверка отдельным таймером:
function dotInObj(dotX, dotY, objX, objY, objWidth, objHeight) {
if ((dotX >= objX && dotY >= objY) && (dotX <= objX + objWidth && dotY <= objY + objHeight)) return true;
else return false;
} //функция проверки "точка в объекте"
function MacroCollision(obj1, obj2) {
var XColl = false;
var YColl = false;
if ((obj1.X + obj1.width >= obj2.X) && (obj1.X <= obj2.X + obj2.width)) XColl = true;
if ((obj1.Y + obj1.height >= obj2.Y) && (obj1.Y <= obj2.Y + obj2.height)) YColl = true;
if (XColl & YColl) {
return true;
}
return false;
}
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var touch = 0;
var objects = [];
var wall = {
X: 0,
Y: 0,
mass: Infinity,
width: 5,
height: cvs.height,
velocity: 0
}
function createObj(mass, velocity, position) { //функция создания объекта, position[0-1]
objects.push({
X: cvs.width * position - 30,
Y: cvs.height - 30,
mass: mass,
width: 30,
height: 30,
velocity: velocity
});
}
createObj(1000, -100, 1);
createObj(1, 0, 0.5); //созаю объекты
ctx.fillStyle = "#000";
let prevTime = 0;
function draw(t) { //функция отрисовки
let dt = (t - prevTime) / 1000;
ctx.clearRect(0, 0, cvs.width, cvs.height); //очистка после предыдущего кадра
ctx.fillRect(wall.X, wall.Y, wall.width, wall.height); //стена
for (let i = 0; i < objects.length; i++) { //отрисовка объектов
ctx.fillRect(objects[i].X, objects[i].Y, objects[i].width, objects[i].height); //заполнение объекта(ов)
objects[i].X += objects[i].velocity * dt; //передвижение объекта(ов) согласно его(их) скорости
}
ctx.font = "16px Arial";
ctx.fillText(touch, 10, 20);
requestAnimationFrame(draw);
prevTime = t;
}
var timer = setInterval(function() {
let j = 1;
for (let i = 0; i < objects.length; i++) {
if (MacroCollision(objects[i], wall)) { //проверка на стык со стеной
objects[i].velocity = Math.abs(objects[i].velocity) * Math.sign(-objects[i].velocity);
touch++;
}
if (MacroCollision(objects[i], objects[j])) { //проверка столкновения 2 объектов
if (j == i) continue; //от бага
let x1 = objects[j].X + objects[j].width;
let x2 = objects[i].X;
let dx = x1 - x2;
if (dx > 0)
dx *= -1;
objects[j].X += dx;
let tx1 = objects[i].velocity;
let tx2 = objects[j].velocity;
let m2 = objects[i].mass + objects[j].mass;
objects[i].velocity = ((objects[i].mass - objects[j].mass) * tx1 + 2.0 *
objects[j].mass * tx2) / m2;
objects[j].velocity = (2.0 * objects[i].mass * tx1 +
(objects[j].mass - objects[i].mass) * tx2) / m2;
touch++;
}
}
}, 1);
requestAnimationFrame(draw);
canvas {
border: 1px solid #999;
}
<div class="canvasBG"></div>
<canvas id="canvas" width="500" height="250"></canvas>
<input type="text" id="console">
Проверка через сетинтервал:
function dotInObj(dotX, dotY, objX, objY, objWidth, objHeight) {
if ((dotX >= objX && dotY >= objY) && (dotX <= objX + objWidth && dotY <= objY + objHeight)) return true;
else return false;
} //функция проверки "точка в объекте"
function MacroCollision(obj1, obj2) {
var XColl = false;
var YColl = false;
if ((obj1.X + obj1.width >= obj2.X) && (obj1.X <= obj2.X + obj2.width)) XColl = true;
if ((obj1.Y + obj1.height >= obj2.Y) && (obj1.Y <= obj2.Y + obj2.height)) YColl = true;
if (XColl & YColl) {
return true;
}
return false;
}
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var touch = 0;
var objects = [];
var wall = {
X: 0,
Y: 0,
mass: Infinity,
width: 5,
height: cvs.height,
velocity: 0
}
function createObj(mass, velocity, position) { //функция создания объекта, position[0-1]
objects.push({
X: cvs.width * position - 30,
Y: cvs.height - 30,
mass: mass,
width: 30,
height: 30,
velocity: velocity
});
}
createObj(1000, -100, 1);
createObj(1, 0, 0.5); //созаю объекты
ctx.fillStyle = "#000";
let prevTime = 0;
function draw(t) { //функция отрисовки
let dt = (t - prevTime) / 1000;
ctx.clearRect(0, 0, cvs.width, cvs.height); //очистка после предыдущего кадра
ctx.fillRect(wall.X, wall.Y, wall.width, wall.height); //стена
for (let i = 0; i < objects.length; i++) { //отрисовка объектов
ctx.fillRect(objects[i].X, objects[i].Y, objects[i].width, objects[i].height); //заполнение объекта(ов)
objects[i].X += objects[i].velocity * dt; //передвижение объекта(ов) согласно его(их) скорости
}
ctx.font = "16px Arial";
ctx.fillText(touch, 10, 20);
requestAnimationFrame(draw);
prevTime = t;
}
var timer = setInterval(function() {
let j = 1;
for (let i = 0; i < objects.length; i++) {
if (MacroCollision(objects[i], wall)) { //проверка на стык со стеной
objects[i].velocity = Math.abs(objects[i].velocity) * Math.sign(-objects[i].velocity);
touch++;
}
if (MacroCollision(objects[i], objects[j])) { //проверка столкновения 2 объектов
if (j == i) continue; //от бага
let x1 = objects[j].X + objects[j].width;
let x2 = objects[i].X;
let dx = x1 - x2;
if (dx > 0)
dx *= -1;
objects[j].X += dx;
let tx1 = objects[i].velocity;
let tx2 = objects[j].velocity;
let m2 = objects[i].mass + objects[j].mass;
objects[i].velocity = ((objects[i].mass - objects[j].mass) * tx1 + 2.0 *
objects[j].mass * tx2) / m2;
objects[j].velocity = (2.0 * objects[i].mass * tx1 +
(objects[j].mass - objects[i].mass) * tx2) / m2;
touch++;
}
}
}, 1);
requestAnimationFrame(draw);
canvas {
border: 1px solid #999;
}
<div class="canvasBG"></div>
<canvas id="canvas" width="500" height="250"></canvas>
<input type="text" id="console">
Видео про это задачу
Вот реализация которая считает правильно в плоть до пары 1e1
1e15
.
Тут фокус в том, чтобы сильно уменьшить шаг между проверками коллизий, когда тяжелый блок прижал легкий к стене.
console.log(Math.PI*1e7);
setTimeout(e => test(1), 100);
function test(mass) {
var cvs = document.createElement('canvas');
cvs.width = 300;
cvs.height = 35;
document.body.append(cvs);
var ctx = cvs.getContext("2d");
var touch = 0;
var wall, o1, o2, objects = [
wall = {X: 0, Y: 0, width: 5, height: cvs.height, mass: Infinity, velocity: 0},
o1 = {X: 100, Y: cvs.height - 20, width: 20, height: 20, mass: 1, velocity: 0},
o2 = {X: 200, Y: cvs.height - 30, width: 30, height: 30, mass: mass, velocity: -250}
];
let i, prevTime = Date.now(), goal = Math.floor(Math.PI*Math.sqrt(o2.mass));
function draw() {
ctx.clearRect(0, 0, cvs.width, cvs.height);
ctx.fillStyle = 'black'
objects.forEach(o => ctx.fillRect(o.X, o.Y, o.width, o.height));
ctx.fillStyle = touch === goal ? 'green' : 'red'
ctx.fillText(touch, 10, 10);
if (o2.X > cvs.width && o1.velocity >= 0 && o1.X > 20) {
clearInterval(i);
if (mass<1e14)
test(mass*100)
} else {
requestAnimationFrame(draw);
}
}
i = setInterval(e => {
tick((Date.now() - prevTime) / 1000);
while (o2.X < wall.width + o1.width) tick(o2.mass>1e5 ? 0.00001 : 0.001)
prevTime = Date.now();
});
function tick(dt) {
objects.forEach(o => o.X += o.velocity * dt);
if (wall.X + wall.width > o1.X) {
o1.velocity = Math.abs(o1.velocity);
touch++;
}
if (o1.X + o1.width > o2.X) {
let dx = o1.X + o1.width - o2.X;
if (dx > 0)
dx *= -1;
o1.X += dx;
let tx1 = o2.velocity;
let tx2 = o1.velocity;
let m1 = o2.mass - o1.mass;
let m2 = o2.mass + o1.mass;
o2.velocity = (m1 * tx1 + 2 * o1.mass * tx2) / m2;
o1.velocity = (2 * o2.mass * tx1 - m1 * tx2) / m2;
touch++;
}
o1.X = Math.max(5, o1.X);
}
requestAnimationFrame(draw);
}
body{margin:0}canvas{background-color:#eee;}
Вот версия, которая работает более менее стабильно, однако ответ не верный...
Так же я изменил секцию где определяется направление движения после столкновения со стеной, она не может быть влево, по этому я убрал второй множитель
function MacroCollision(obj1, obj2) {
return obj1.X + obj1.width >= obj2.X && obj1.X <= obj2.X + obj2.width;
}
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var touch = 0;
var objects = [];
var wall = {
X: -1000,
Y: 0,
mass: Infinity,
width: 1005,
height: cvs.height,
velocity: 0
}
function createObj(mass, velocity, position) { //функция создания объекта, position[0-1]
objects.push({
X: cvs.width * position - 30,
Y: cvs.height - 30,
mass: mass,
width: 30,
height: 30,
velocity: velocity
});
}
createObj(10000, -50, 1);
createObj(1, 0, 0.5); //созаю объекты
ctx.fillStyle = "#000";
let prevTime = Date.now();
function draw(t) { //функция отрисовки
ctx.clearRect(0, 0, cvs.width, cvs.height); //очистка после предыдущего кадра
ctx.fillRect(wall.X, wall.Y, wall.width, wall.height); //стена
for (let i = 0; i < objects.length; i++) { //отрисовка объектов
ctx.fillRect(objects[i].X, objects[i].Y, objects[i].width, objects[i].height); //заполнение
}
ctx.font = "16px Arial";
ctx.fillText(touch, 10, 20);
requestAnimationFrame(draw);
}
var timer = setInterval(function() {
let dt = (Date.now() - prevTime) / 1000;
prevTime = Date.now();
let j = 1;
for (let i = 0; i < objects.length; i++) {
objects[i].X += objects[i].velocity * dt; //передвижение объекта(ов) согласно его(их) скорости
objects[i].X = Math.max(5, objects[i].X)
if (MacroCollision(objects[i], wall)) { //проверка на стык со стеной
objects[i].velocity = Math.abs(objects[i].velocity);
touch++;
}
if (MacroCollision(objects[i], objects[j])) { //проверка столкновения 2 объектов
if (j == i) continue; //от бага
let x1 = objects[j].X + objects[j].width;
let x2 = objects[i].X;
let dx = x1 - x2;
if (dx > 0)
dx *= -1;
objects[j].X += dx;
let tx1 = objects[i].velocity;
let tx2 = objects[j].velocity;
let m2 = objects[i].mass + objects[j].mass;
objects[i].velocity = ((objects[i].mass - objects[j].mass) * tx1 + 2.0 *
objects[j].mass * tx2) / m2;
objects[j].velocity = (2.0 * objects[i].mass * tx1 +
(objects[j].mass - objects[i].mass) * tx2) / m2;
touch++;
}
}
});
requestAnimationFrame(draw);
<canvas id="canvas" width="500" height="150" style="border: 1px solid #999"></canvas>
Виртуальный выделенный сервер (VDS) становится отличным выбором
Каким образом на Css можно реализовать фигуру, которая изображена снизу на картинке
Если проект разбит на множество файлов, описывающих разных наследников некоторого абстрактного класса, то в файле, в котором нужно использовать...