включить дельту времени и скорость в движение скорости / трения - PullRequest
1 голос
/ 14 марта 2019

Я экспериментирую с механикой движения в простой игре и нашел очень приличную статью с примером того, как сделать ее «реалистичной», играя со скоростью и трением (это очень популярный подход). Таким образом, объект начинает медленно, ускоряется до предела, и как только вы отпускаете ключ, он начинает терять скорость до 0.

ключевая часть добавляет «газ»:

if(keys[38]) {
    spaceship.ax = Math.cos(spaceship.r) * 0.05;
    spaceship.ay = Math.sin(spaceship.r) * 0.05;
} else {
    spaceship.ax = spaceship.ay = 0;
}

и использование этого дросселя для увеличения скорости, ограниченной трением

var friction = 0.97;
function updatePosition(obj) {
    //update velocity
    obj.vx += obj.ax;
    obj.vy += obj.ay;

    //apply friction
    obj.vx *= friction;
    obj.vy *= friction;

    //update position
    obj.x += obj.vx;
    obj.y += obj.vy;
}

Хотя это выглядит очень красиво и прилично, это нарушает мою логику в отношении движения, основанного на времени. Он обязателен, чтобы игрок мог видеть скорость в секунду, поскольку он позволяет планировать обновления, планировать поездки и расход топлива.

Итак, текущая реализация выглядит так:

this.now = undefined;
this.delta = undefined;
this.then = Date.now();

this.setMoveDelta = function() {
  this.now = Date.now();
  this.delta = (this.now - this.then) / 1000;
  this.then = this.now;
};

this.speed = 100; // 100 pixels per second
this.move = function() {
    this.setMoveDelta();
    var partialDistance = this.speed * this.delta;
    this.x += Math.cos(this.rad) * partialDistance;
    this.y += Math.sin(this.rad) * partialDistance;
}

Теперь, когда вы запускаете включенную демо-версию, можно заметить, что из-за фрикционной крышки наблюдается своего рода «максимальная» скорость. Вопрос в том, можно ли как-нибудь установить эту шапку на this.speed * this.delta? или использовать какой-то другой подход, чтобы заставить корабль двигаться с максимальной скоростью с объявленными параметрами (например, 100 пикселей в секунду)?

Идея состоит в том, чтобы оставить ускорение и замедление как есть, но как только корабль достигнет максимальной скорости, это будет объявленная скорость (и отображается для пользователя). Затем эта скорость используется для расчета времени, необходимого для перемещения из пункта А в пункт В, и количества топлива, которое будет использовано. В настоящее время это кажется случайным.

var canvas = document.createElement('canvas'),
	ctx = canvas.getContext('2d'),
	w = 400,
	h = 400;
canvas.width = w;
canvas.height = h;

document.body.appendChild(canvas);

var spaceship = {
	x: w / 2, y: h / 2,
	vx: 0, vy: 0,
	ax: 0, ay: 0,
	r: 0,
	draw: function(){
		ctx.save();
		ctx.translate(this.x, this.y);
		ctx.rotate(this.r);
		ctx.fillStyle = 'white';
		ctx.fillRect(-10, -5, 20, 10);
		ctx.restore();
	}
};

var friction = 0.97;

function updatePosition(obj) {
	//update velocity
	obj.vx += obj.ax;
	obj.vy += obj.ay;

	applyFriction(obj);

	//update position
	obj.x += obj.vx;
	obj.y += obj.vy;
}

//user interactivity

var keys = [];
document.addEventListener('keydown', function(e){
	keys[e.which] = true;
});
document.addEventListener('keyup', function(e){
	keys[e.which] = false;
});

function applyFriction(obj){
	obj.vx *= friction;
	obj.vy *= friction;
}

(function animloop(){
	requestAnimationFrame(animloop, canvas);
	ctx.fillStyle = '#000';
	ctx.fillRect(0, 0, w, h);

	//rotation
	if(keys[37]) spaceship.r -= 0.05;
	if(keys[39]) spaceship.r += 0.05;

	//thrust
	if(keys[38]){
		spaceship.ax = Math.cos(spaceship.r) * 0.05;
		spaceship.ay = Math.sin(spaceship.r) * 0.05;
	}else{
		spaceship.ax = spaceship.ay = 0;
	}

	updatePosition(spaceship);
	spaceship.draw();
})();

----- редактировать

Я реализовал предложенное решение, но даже с этой формулой максимальная скорость немного меньше заявленной, в результате чего другие объекты движутся с фактической скоростью 100px в секунду, а в долгосрочной перспективе просто быстрее. это новое демо:

this.now = undefined;
this.delta = undefined;
this.then = Date.now();

this.setMoveDelta = function() {
  this.now = Date.now();
  this.delta = (this.now - this.then) / 1000;
  this.then = this.now;
};

this.speed = 100; // 100 pixels per second

var secondObj = {
	x: 0,
	y: 250,
	r: 0,
	active: false,
	draw: function(){
		ctx.save();
		ctx.translate(this.x, this.y);
		ctx.rotate(this.r);
		ctx.fillStyle = 'white';
		ctx.fillRect(-10, -5, 20, 10);
		ctx.restore();
	}
};

var canvas = document.createElement('canvas'),
	ctx = canvas.getContext('2d'),
	w = 1200,
	h = 400;
canvas.width = w;
canvas.height = h;

document.body.appendChild(canvas);

var spaceship = {
	x: 0, y: 200,
	vx: 0, vy: 0,
	ax: 0, ay: 0,
	r: 0,
	draw: function(){
		ctx.save();
		ctx.translate(this.x, this.y);
		ctx.rotate(this.r);
		ctx.fillStyle = 'white';
		ctx.fillRect(-10, -5, 20, 10);
		ctx.restore();
	}
};

var friction = 0.97;

function updatePosition(obj) {
	//update velocity
	obj.vx += obj.ax;
	obj.vy += obj.ay;

	applyFriction(obj);

	//update position
	obj.x += obj.vx;
	obj.y += obj.vy;
}

//user interactivity

var keys = [];
document.addEventListener('keydown', function(e){
	keys[e.which] = true;
	setTimeout(function() {
	secondObj.active = true;
	}, 600);
});
document.addEventListener('keyup', function(e){
	keys[e.which] = false;
});

function applyFriction(obj){
	obj.vx *= friction;
	obj.vy *= friction;
}

var is = function(c, num) {
	if(parseInt(c) < num + 1 || parseInt(c) > num - 1) {
	   return true;
	}
	return false;
};

(function animloop(){
	requestAnimationFrame(animloop, canvas);
	ctx.fillStyle = '#000';
	ctx.fillRect(0, 0, w, h);

	//rotation
	if(keys[37]) spaceship.r -= 0.05;
	if(keys[39]) spaceship.r += 0.05;

	//thrust
	this.setMoveDelta();
	if(keys[38]){
		spaceship.ax = Math.cos(spaceship.r) * (this.speed * this.delta * (1-0.97));
		spaceship.ay = Math.sin(spaceship.r) * (this.speed * this.delta * (1-0.97));
	}else{
		spaceship.ax = spaceship.ay = 0;
	}
	
	updatePosition(spaceship);
	spaceship.draw();
	
	if(secondObj.active) {
	secondObj.x += Math.cos(0) * ( this.speed * this.delta );
}
	secondObj.draw();
})();

1 Ответ

1 голос
/ 14 марта 2019

Теперь скорость равна root_of (obj.vx ^ 2 + obj.vy ^ 2) /this.delta, а максимальная скорость равна 0,05 / (1-0,97) /this.delta.Первое, так как vx и vy находятся в движении за каждый раз дельта.Последнее, поскольку увеличение скорости на 0,05 уравновешивается снижением скорости * (1-0,97).

Ответ заключается в том, что ограничение скорости может быть установлено путем регулировки константы ускорения 0,05 или константы трения 0,97.Давайте используем ускорение:

Константа ускорения = max_speed * this.delta * (1-0.97)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...