Согласно рекомендациям @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>
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Каким образом на Css можно реализовать фигуру, которая изображена снизу на картинке
Если проект разбит на множество файлов, описывающих разных наследников некоторого абстрактного класса, то в файле, в котором нужно использовать...