4
\$\begingroup\$

Good evening.

I apologize if the title is a bit vague, I really could not come up with anything better. I am currently reading a book called the nature of code and as a side project, I am working on a small 2d game in javascript.

One of the core concepts of this book states that a game object should incorporate three vectors, acceleration, velocity and location, where only acceleration is directly modified by the player. This translates to the following code;

    acceleration.add(0, 0.001);
    velocity.add(acceleration);
    location.add(velocity);

This works perfectly fine and so does the the .sub() method, when I want to move backwards. However, this requires two functions. Also, all vectors have to be positive. This is quite a pain because I now have to decide whether the applied vector is a positive or a negative one, thus requiring methods like applyForceNeg(vec) and applyForcePos(vec). I'd much rather have one method where I could pass any vector.

I have pasted some of the code here.

How would I implement this without turning the code into a complete mess?

Any help would be much appreciated.

EDIT:

I figured out what actually caused all this;

As you can see here, I initially wrote the if statement so that upon every run, acceleration gets incremented by 0.001. Then acceleration gets added to velocity, which in turn gets added to location. The problem was that I did not set acceleration back to zero. So in the first run, acceleration is set to 0.001, so is velocity and location. However, in the second run, acceleration increases by 0.001, yielding a total value of 0.002 which in turn gets added to velocity and location. Therefore we now have acceleration = 0.002, velocity = 0.003 and location = 0.003. After the third run, velocity and location are at 0.006 while acceleration is at 0.003.

Now, when I set acceleration to zero, it reduces the problem. Only if I also reset velocity, the problem is gone. However, I doubt this is the right way to do it...

\$\endgroup\$
1
  • \$\begingroup\$ What is the problem, currently? \$\endgroup\$
    – Attackfarm
    Commented May 4, 2013 at 21:03

5 Answers 5

1
\$\begingroup\$

First, you shouldn't be hard-coding numbers like your acceleration. Numbers like these, which need to be fine tuned, should be variables that can be easily tweaked, either in one-place or dynamically using a slider or somesuch during testing.

Second, from what I understand (of your comment to MagiSun), your problem with the acceleration giving you odd behavior is that your acceleration value is far too small.

Even at 60 updates per second, you're accelerating at 0.06/s, and unless your velocities and positions are also in similarly small measurements (which I assume they aren't by your description of the resultant behavior), this will result in a very slow deceleration.

If you're keeping the "Down" key pressed the entire time, by the time you reverse course (and travel in negative velocity), your negative acceleration will be very noticeable, giving you the "uncontrollable speeds".

Try raising your acceleration value, but take note that you might also want to increment acceleration at a slower rate than your updates-per-second/fps. Another concern is that you might want a maximum acceleration, even if it might be unrealistic, simply for the sake of a good "feel".

\$\endgroup\$
2
  • 1
    \$\begingroup\$ After some trial and error, this actually fixed it just now. It really was about setting a higher accerleration value. I had used 0.001 and 0.1 really did the trick. Thanks a lot! \$\endgroup\$
    – user30340
    Commented May 4, 2013 at 21:09
  • \$\begingroup\$ Glad I could help =) \$\endgroup\$
    – Attackfarm
    Commented May 4, 2013 at 21:28
1
\$\begingroup\$

In regards to this comment by OP:

The problem is the resulting behavior, where the object continues to move up for example, even if the down key is pressed. It eventually changes direction but by then it's at nearly uncontrollable speeds.

That is how velocity and acceleration work. You are simply providing the user with acceleration increments that are too large. Try reducing your acceleration increments to 1/4 of their present value, and report the results.

Because distance travelled varies as the square of applied acceleration, rather subtle changes in acceleration can affect the agent more than might be intuitively expected. Also check my answer to this post on simple equations of motion with constant acceleration.

Update:
Your turns should look like this, where the player can only set a = .001, or 0.000 or -0.001:

    a    v    l
 0.001 .001 .001
 0.001 .002 .003
 0.001 .003 .006
 0.001 .004 .010
 0.001 .005 .015 now motor turned off, so a = 0.000
 0.000 .005 .020
 0.000 .005 .025 now retro-rockets fired (ie braking applied)
-0.001 .004 .029
-0.001 .003 .032
-0.001 .002 .034
-0.001 .001 .035
-0.001 .000 .036 now motor turned off again 
 0.000 .000 .036 etc.

When you increase the acceleration by .001 each time, you are in fact using constant jerk, as I suspected earlier.

\$\endgroup\$
6
  • \$\begingroup\$ Yes, I think the problem is the OP's understanding of acceleration. Though I wouldn't cut the acceleration down, or it'll exacerbate the problem \$\endgroup\$
    – Attackfarm
    Commented May 4, 2013 at 19:18
  • \$\begingroup\$ @Attackfarm: Disagree strongly with the last part. The problem is that the velocity is becoming unreasonably high, which only happens because acceleration is either too great, or applied for too long. For example, even Usain Bolt takes 60m to reach full speed. Short of applying a drag proportional to v^2, only by scaling back acceleration can the distances travelled be scaled back. \$\endgroup\$ Commented May 4, 2013 at 19:22
  • \$\begingroup\$ But he mentions specifically that "it eventually changes direction", and the hard-coded value is 1/1000. Unless every value he uses is incredibly small, what he's finding is pressing the "down" key constantly until the ship finally reverses, at which point the acceleration is so high that the reversed velocity is similarly high. \$\endgroup\$
    – Attackfarm
    Commented May 4, 2013 at 19:36
  • 1
    \$\begingroup\$ That sounds like the equations of motion are incorrect, and OP is providing control of jerk instead of acceleration. (Jerk is rate of change of acceleration, the third derivative of distance instead of the second.) \$\endgroup\$ Commented May 4, 2013 at 19:39
  • \$\begingroup\$ What Attackfarm stated is correct. There is a reversal, though by that time, the velocity is far too high. I edited my original question, which I believe now states the real problem. I'm really just using things I learned from the book I mentioned. For example, he explicitly states that one should use very small values for acceleration. \$\endgroup\$
    – user30340
    Commented May 4, 2013 at 20:51
0
\$\begingroup\$

So, the .add method doesn't take negative numbers?

If it does, it's just a matter of adding a negative acceleration to velocity. Otherwise, you really have no choice but to check.

EDIT: I think I understand your problem now. Vectors can't be negative correct? If so, you'd probably be better off having acceleration be two scalars. This way, I assume they can be negative and you can just add each component.

\$\endgroup\$
6
  • \$\begingroup\$ The .add() method takes numbers as well as a vector object. It is really nothing fancy and should actually take negative values just fine. Adding a negative value should be the same as subtracting a positive one, right? However, as soon as I use negative values, the vectors continue to increase or decrease exponentially, leading to a pretty much uncontrollable object. It's on c9.io, I can post the address if you don't mind the horrible state of code. :) \$\endgroup\$
    – user30340
    Commented May 4, 2013 at 17:59
  • \$\begingroup\$ Yes, adding a negative number is the same as subtracting a positive number. Doing it this way SHOULD be fine. What's with this line though? acceleration.mult(0); \$\endgroup\$
    – user30331
    Commented May 4, 2013 at 18:03
  • \$\begingroup\$ Okay, now I know what's going on. When you mult acceleration by 0, you're getting rid of it for good. That's not the idea behind acceleration. It's meant to keep going even when you're not pressing a button. Like a falling object, it keeps getting faster and faster, and doesn't continue at the same speed. What you want to do is get rid of that mult line, and make the opposite direction invoke a negative value. This will give you the acceleration effect. \$\endgroup\$
    – user30331
    Commented May 4, 2013 at 18:09
  • \$\begingroup\$ That's also from said book. I'm using this project pretty much as a playground. This particular line is supposed to set the accerlaration to zero. It does have a slight effect but it's not really related to this problem. \$\endgroup\$
    – user30340
    Commented May 4, 2013 at 18:09
  • \$\begingroup\$ If you want to move at a constant speed, you'll want that line for both key presses. If you want acceleration, however, you'll need to do as I explained above. \$\endgroup\$
    – user30331
    Commented May 4, 2013 at 18:11
0
\$\begingroup\$

Have you thought about adding negative velocity and using a ternary statement to decide what to add?

if(keys.up != keys.down) {
  acceleration.add(0, keys.up ? 0.001 : -0.001);
  velocity.add(acceleration);
  location.add(velocity);
}

EDIT: What platform are you using? I've never seen a vector class that couldn't go negative.

\$\endgroup\$
2
  • \$\begingroup\$ Sorry, I may have phrased that badly. The Vector class can do that just fine. The problem is the resulting behavior, where the object continues to move up for example, even if the down key is pressed. It eventually changes direction but by then it's at nearly uncontrollable speeds. \$\endgroup\$
    – user30340
    Commented May 4, 2013 at 18:05
  • 1
    \$\begingroup\$ You might want to edit your question to reflect your new question, if the behavior, and not the logic of subtracting vectors, is the real problem. \$\endgroup\$
    – Attackfarm
    Commented May 4, 2013 at 18:48
0
\$\begingroup\$

I see several things wrong with your code. However, before trying to list them here, let me just show how I would rewrite your main loop:

// reset acceleration to default value
acceleration.set(0, 0);  // or, equivalently, acceleration.mult(0);

// adjust acceleration based on keys pressed
if (keys.up) {
    acceleration.add(0, 0.001);
}
if (keys.down) {
    acceleration.sub(0, 0.001);
}

// move ship according to Newton's laws
velocity.add(acceleration);
location.add(velocity);

ship.move(location.getX(), location.getY());

The main differences between this and your code are that:

  1. The acceleration is not retained between update steps, but is reset to its default value (which I've assumed to be zero, but you could e.g. include gravity in that value) on every iteration. This default value is then modified based on the player's controls.

  2. The velocity and location of the ship are updated on every iteration, not just when the player presses a key: in the absence of player interaction, the ship should continue on its inertial trajectory. Of course, since the default acceleration is zero, the velocity only changes if the player presses some keys (and thus the inertial trajectory is a straight line), but that would change if you included gravity.

Note that I've assumed that you want the ship to accelerate upwards when the up key is pressed and downwards if the down key is pressed, regardless of its orientation. Depending on what kind of game you're making, you may instead want to add or subtract the ship's heading vector (normalized and scaled by a constant) instead of the fixed vector (0, 0.001) to the acceleration.


Also, there are a couple of improvements you could make to you physics simulation (i.e. the lines velocity.add(acceleration); and location.add(velocity);). One is to account for the fact that the velocity has changed during the timestep in your location update, by taking the average of the old and new velocity:

// move ship according to Newton's laws...
velocity.add(acceleration);
location.add(velocity);
// ...including the correction for the gradual change in velocity
acceleration.mult(0.5);
location.sub(acceleration);

This makes the trajectories physically exact (under constant acceleration), and, in particular, ensures that, with appropriate scaling, they won't be affected by changes in the simulation time step. However, to really make use of this feature, you need to actually involve the timestep in the calculations. This would be easiest to do if your vector class included a multiply-and-add method:

// move ship according to Newton's laws...
velocity.muladd(acceleration, timestep);  // velocity += acceleration * timestep
location.muladd(velocity, timestep);      // location += velocity * timestep
// ...including the correction for the gradual change in velocity
location.muladd(acceleration, -timestep*timestep/2);

If not, you'll just have to do it by hand, e.g. like this:

// velocity += acceleration * timestep
acceleration.mult(timestep);
velocity.add(acceleration);

// location += (velocity - 0.5 * acceleration * timestep) * timestep
var displacement = velocity.clone();
acceleration.mult(0.5);
displacement.sub(acceleration);
displacement.mult(timestep);
location.add(displacement);

Note that this will mess up the acceleration vector (by multiplying it with 0.5 * timestep²), so you shouldn't use it for anything after this.

\$\endgroup\$
1
  • \$\begingroup\$ Now that is a thorough answer. Thank you. :) I've pretty much fixed the problem thanks to Attackfarm though you are right, the code is pretty messy. When dealing with something completely new, I usually don't care too much about looks or structure as I can simply rewrite it when I get a better understanding of how to do things properly. For example, now that it works, I will write a gameObj class and most of this will be wrapped in the gameObj.applyForce(v); function. Thanks again for your help. It's much appreciated. :) \$\endgroup\$
    – user30340
    Commented May 5, 2013 at 9:13

You must log in to answer this question.