代码之家  ›  专栏  ›  技术社区  ›  Mevia

将时间增量和速度纳入速度/摩擦运动

  •  1
  • Mevia  · 技术社区  · 5 年前

    我正在简单的游戏中尝试运动力学,我发现了一篇非常不错的文章,其中有一个例子,说明了如何通过使用速度和摩擦(非常流行的方法)使其“逼真”。所以物体开始缓慢,加速到一个上限,一旦你松开键,它开始减速,直到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像素)使船舶以最大速度移动?

    其想法是保持加速和减速不变,但一旦船舶达到最大速度,它将是声明的速度(并向用户显示)。然后,该速度用于计算从A点到B点行驶所需的时间,以及将使用多少燃油。现在感觉很随意。

    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();
    })();
    0 回复  |  直到 5 年前
        1
  •  1
  •   Mats Lind    5 年前

    现在速度是(obj.vx^2+obj.vy^2)/这个的根。增量和最大速度为0.05/(1-0.97)/这个。希腊字母表的第4个字母第一次是vx和vy每次都在移动增量。后者是因为速度增加0.05与速度降低*(1-0.97)相平衡。

    答案是速度上限可以通过调整加速度常数0.05或摩擦常数0.97来设置。让我们使用加速:

    加速度常数=最大速度*这个。δ*(1-0.97)