Reputation: 1564
I am experimenting with movement mechanics in simple game and i found very decent article with example of how to make it "realistic" by playing with velocity and friction (its very popular approach). So the object is starting slow, speeding up to a cap and once you release key it starts to loose speed until 0.
key part is adding "throttle":
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;
}
and utilising that throttle to increase speed capped by friction
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;
}
Although it looks very nice and feels decent, it breaks my logic that regards move based on time. Its mandatory so player can see the speed per second, as it allows to make planned upgrades, plan travels and fuel usage.
So current implementation looks like this:
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;
}
Now when you run included demo it can be noticed that there is sort of "max" speed due to friction cap. The question is can this cap be set to this.speed * this.delta
somehow? or use some other approach to make ship move at max speed with declared parameters (for example 100 px per second) ?
The idea is to leave accelerating and slowing down as is but once ship reaches max speed it would be the declared speed (and displayed for user). That speed is then used to calculate time needed to travel from point A to B and how much fuel will be used. Currently it feels random.
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();
})();
-----edit
I implemented suggested solution but even with this formula, max speed is slightly less then declared resulting other objects moving with actual 100px per second, being simply faster in long run. this is new demo:
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();
})();
Upvotes: 1
Views: 956
Reputation: 934
Now speed is root_of(obj.vx^2 + obj.vy^2)/this.delta and max speed is 0.05/(1-0.97)/this.delta. The first since vx and vy is in movement per each time delta. The latter since speed increase of 0.05 is balanced by speed decrease of speed*(1-0.97).
The answer is that speed cap could be set by adjusting eithe the acceleration constant 0.05 or the friction constant 0.97. Let's use the accelereration one:
acceleration constant = max_speed * this.delta * (1-0.97)
Upvotes: 1