2019-01-29 09:11:27

Your calculator is correct. BGT is giving you cos and sin for theta=0. I can't find why it would be doing so, given the shared code and logs.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-01-30 06:52:00 (edited by JLove 2019-01-30 08:43:25)

Just out of curiosity and as a further effort to troubleshoot this, I created a totally separate tiny program with only the code for theta to see whether BGT would get it correct.  Code is:
double theta;
double deg = 253;
const double pi = 3.14159265358979;

void main()
{
show_game_window("");
DTR(deg);
alert("", "Number of degrees is " + deg + ".  Theta is " + theta + ".  Cosine of theta is " + cosine(theta) + ".  Sine of theta is " + sine(theta) + ".");
}

double DTR(double deg)
{
return theta = deg*pi/180;
}
It seems to work now.  This is what it shows:
Number of degrees is 253.  Theta is 4.41568300754565.  Cosine of theta is -0.292371704722741.  Sine of theta is -0.956304755963034.
That is correct.  If I change the number of degrees to, say, 75, I get:
Number of degrees is 75.  Theta is 1.30899693899575.  Cosine of theta is 0.258819045102522.  Sine of theta is 0.965925826289068.
I double checked with a calculator, and those are both correct.
UPDATE  I think I may have stumbled across the issue.  I put a log entry in every function where theta was used.  It actually shows as correct in the check and move functions, but for some reason it's not actually performing the correct math on the vector.  I used arbitrary numbers just for testing.  Log shows:
In check function.  Number of degrees is 253.  Theta is 4.41568300754565.  Cosine of theta is -0.292371704722741, and sine of theta is -0.956304755963034.
(In move function.  Number of degrees is 253, theta is 4.41568300754565, cosine of theta is -0.292371704722741, sine of theta is -0.956304755963034, accelleration x is 100, accelleration y is 100, mass is 0.057000000029802.  Velocity x is 1754.38598632813, velocity y is 0, and velocity z is -9.8100004196167.
Velocity Y should not be 0, and vel.x is wrong as well.  My calculator says that (100/0.057), or (acc/M2) is 1754.385964912280.  It shows vel.x as 1,754...so, it got that part right.  However, the correct math is (acc/M2)*cosine(theta).  So, (100/0.057)*-0.292371704722741, which does not equal 1,754.  It equals -512.93281530305438596491228070175, according to my calculator.  This means that for some reason it appears as though the part of the math "*cosine(theta)" is being ignored.

2019-01-30 09:53:22

Is vel being updated correctly? It wasn't clear from the shared snippet what the context was. If vel is only assigned a value on creation, before theta has been set, that would do it.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-01-30 21:31:10 (edited by JLove 2019-01-30 21:37:55)

Yeah, that's what I thought days ago, actually.  So I made a function that looks like this:
vector UpdateVel()
{
vel.x = (acc.x/M2)*cosine(theta);
vel.y = (acc.y/M2)*sine(theta);
vel.z = -9.81;
return vel;
}
Then, apparently I forgot to place it in the check function, like a damn idiot.  No wonder it never worked.  So I just placed it in the check function before moving the ball, and now it works correctly:
In move function.  Number of degrees is 253, theta is 4.41568300754565, cosine of theta is -0.292371704722741, sine of theta is -0.956304755963034, accelleration x is 100, accelleration y is 100, mass is 0.057000000029802.  Velocity x is -512.932800292969, velocity y is -1677.72766113281, and velocity z is -9.8100004196167.
Ball is now at -10.2586555480957, -33.5545539855957, 499.803802490234.
It appears to be seeing it correctly, but to be thorough, as a further test, I changed degrees to 45, which should be a 1:1 ratio on x and y.  And that appears to work as well:
In move function.  Number of degrees is 45, theta is 0.785398163397448, cosine of theta is 0.707106781186548, sine of theta is 0.707106781186547, accelleration x is 100, accelleration y is 100, mass is 0.057000000029802.  Velocity x is 1240.53820800781, velocity y is 1240.53820800781, and velocity z is -9.8100004196167.
Ball is now at 24.8107643127441, 24.8107643127441, 499.803802490234.
Finally!  For the record, I feel like a total fucking idiot, because I suspected that this was the issue, then wrote the function above, only to forget to add it to the check function.  And when it didn't work, I assumed my first suspicion was wrong, when it actually wasn't.  I really feel like a damn moron.  Here's the question, though.  I really, really would prefer not to write functions to update each vector like that.  Is there a way to write a generic function that will update any vector I choose?  That way I don't need to write one for velocity, one for drag, one for acceleration, etc.  Again, I cannot believe how stupid I feel at the moment.  Wow.  Just...wow.  It's times like this when I seriously question whether I am smart enough to complete this.  What a basic, totally moronic mistake.  Wowzers.

2019-01-31 09:19:15

One of BGT's more annoying issues is the lack of the 'new' keyword or equivalent, which would probably help.
I think there's a new_vector function in my math.bgt script, something like:
vector new_vector (double x=0, double y=0, double z=0) {
  vector ret (x, y, z);
  return ret;
}
Then I got tired of writing new_vector so much, and made another just like it called nv.
Not sure if this is quite what you were talking about.
It's surprisingly easy to forget things like that without realizing it until doing a deep dive for some other problem. I still do this on occasion. Sometimes it will be something like this, other times it will be things like forgetting to set how much damage an attack should do, and I seem to remember at least once forgetting to add a move to a movelist after creating it. All of those being recent.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-01-31 09:40:08

What I want is a way to update any vector on the fly, so to speak.  Ok, I've gotten everything working correctly in terms of direction.  Now what I need to know is how best to mirror the direction for the opponent machine.  In other words, if I hit the ball at a 45 degree angle away from me, it's actually going to come toward the opponent, so the movement would basically be mirrored.

2019-01-31 13:53:03

You could have the game determine who is "player1" and "player2", and actually position them differently, but that'd probably be more arduous than just mirroring. I'm actually not too well versed in this, although it should be simple.
Ex, to mirror a vector across the origin, effectively rotating 180degrees, you just multiply by negative 1. I found out yesterday that BGT's vectors lack an OpNeg method, and that somehow doesn't just resolve to multiplying by -1, so this would require -1*v, where v is the vector.
Mirroring over a specific axis is a bit more complex, by which I mean I manage to screw it up in spite of it not being that complex. If you work with vectors, just multiply the value for that axis by -1. If you want to mirror theta, I can never remember the correct way and have to rederive it every time I need it. Ex, to mirror across the x axis, you'd set theta = 2*pi-theta. Y is more troublesome. Like, I think pi-theta works, and sine/cosine shouldn't care if theta is between 0 and 2pi, but if you need theta to be between 0 and 2pi, you can either say if (theta < 0) theta += 2pi, or just say theta = (3pi - theta) % 2pi.
And I have no idea if any of that does what you need. Strictly speaking, you have an affine transform that translates and scales. And affine transforms are kinda confusing.
To try and make this more useful: is the net on or parallel to one of the axes? And how far apart are the players? I think applying those axis flips, then adding the distance, should get the job done. So if the net is on the y axis, and the players are 10m apart, x-wise, I'd try something like:
void mirror (ball@ b) {
  b.theta = (3pi - b.theta) % 2pi;
b.vel.y *= -1
b.position.x += 10;
// if y needs to be mirrored on the ball's position, b.position.y *= -1, assuming that the center of the court is at y=0.
}
I feel like this was a confusing mess of a post. hmm Hopefully there's something useful here?

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-01-31 21:59:06 (edited by JLove 2019-02-01 04:38:06)

Both X and Y will have to be mirrored, as the ball will travel along both axes.  If I hit the ball at a 60 degree angle, it's gonna travel along both.  If I hit a 230 degree angle shot, it's gonna travel along both.  I've adjusted to use the 12 o'clock system because of the way that the BGT sound pool does things.  That wasn't hard, just took modifying one line of code:
return theta = (90-deg)*pi/180;
And it works fine.  Now, 90 degrees travels along y only to the right, 45 degrees is a 1 to 1 ratio of x to y to the right, 270 degrees travels along Y only to the left, and 315 degrees is a 1 to 1 ratio of x to y to the left.  I've tested it, and it works fine.  Just need to make sure that when the ball travels away from player 1, it goes toward player 2, and that would seem to best be accomplished by mirroring.  Which would mean that a 45 degree shot going away from me would be travelling at 225 degrees toward the opponent, if my math is correct.
As for the net, it sits along the x axis, since x travels left to right in this case, in the center of the court.  X goes left to right, y goes forward and backward, and z for height of the ball.  I think that the normal convention is that y represents height, but I have seen both, so I'm guessing that either can be used, and for whatever reason my brain likes z for height.
UPDATE:  I tried to simulate mirroring in my test program by having the ball return to me after it landed.  I tried multiplying velocity by negative 1 as you suggested in your post.  Relevant code:
if(B.pos.z <= 0 and B.pos.x > 0 and B.pos.y > 0)
{
alert("", "returning to original coordinates.");
B.pos.z = 50;
vel.x *= -1;
vel.y *= -1;
B.pos += vel*DT;
}
It did not work.  The ball just continued to travel further away along the same angle.  In other words, the angle remained 45 degrees in this case, but the ball continued to travel away from me, not back toward me.  So I obviously did something wrong.  I must have misunderstood your post.

2019-02-01 22:41:44 (edited by JLove 2019-02-02 05:42:12)

UPDATE:  Adding 180 degrees and recalculating theta mirrors the angle correctly:
B.pos.z = 50;
deg += 180;
//recalculate theta.
DTR(deg);
B.moving = true;
move();
And the previous movement is mirrored.  I do notice that apparently as height lowers, it never equals 0, because the block of code that starts:
if(B.z == 0)
never executes, and when I check after a while, B.z is way, way less than 0, like negative 35 or so.  So I changed it to:
if(B.z <= 0)
and then it works, but a lot of the time it waits until it is negative before executing.  So I had to say:
if(B.z <= 0)
B.z = 0;
I don't understand why Z is never just 0.  Theoretically, as the ball travels, z has to hit exactly 0 at some point, and when that happens, the line if(B.z == 0) should execute.  Changing it to:
if(B.z <= 0)
does allow the ball to stop.  One other thing I do notice that quite frankly royally pisses me off is that when mirroring, the ball doesn't land where it started.  In other words, ball starts at position 0, 0, 50.  After travelling at 45 degrees, it lands at 31.0134830474854, 31.0134830474854, 0.  After mirroring, which should bring it right back to where it was on x and y, at 0, 0, it shows that ball lands at 0.248107805848122, 0.248107805848122, 0.  Why the discrepancy?  Shouldn't it travel the same exact distance on each side?  Numbers are immutable, as is the math done upon those numbers. 2 plus 2 will always be four, 2 times 2 will always be 8, and so on.  I don't understand the discrepancy here, since the only thing that I did was exactly mirror the previous shot, with no other changes.  It should be like driving a car to the store down the street, then back home, the same distance for both, right?  These sort of discrepancies are what cause the problem I have that has forced me to entirely rewrite this code in the first fucking place.  I thought that using vectors and applying actual immutable math equations to this would stop this sort of shit.  So what the fuck is causing the discrepancy now?
UPDATE 2:  I ran the test 5 times, and the numbers for x and y came out exactly the same each time.  Before I move, I set degrees to 45.  Starting cords for the vector position of the ball are x at 0, y at 0, and z, for height, at 50.  Degrees set to 45, and the test commences.  Endin position is x at 31.0134830474854, y at 31.0134830474854, z at 0.  When mirroring, z is set back to 50, and the return begins.  However, the position ends with x at 0.248107805848122, y at 0.248107805848122, and z at 0.  So at least the discrepancy itself is a constant one.  In five tests, those coordinates matched exactly all 5 times.  Theoretically, though, shouldn't the ball return to exactly 0 x and 0 y where it started?  There shouldn't be any discrepancy, given that height was set to exactly the same in both cases, right?  Math doesn't change, and neither does distance.  If distance A = 5, then if distance b is simply a mirror of a, it should equal 5 as well, right?

2019-02-04 23:43:10 (edited by JLove 2019-02-04 23:45:56)

Ok, so I've begun incorporating the new code into my existing code, and for the most part things appear to be working correctly in terms of movement, although the distance discrepancy stated in the above post still confuses me.  Now, what is the best way to handle the bounce of the ball on the court, approach angle and rebound angle?

2019-02-13 22:13:48 (edited by JLove 2019-02-13 22:17:04)

Ok, so what the hell.  Trying something new to see whether velocity would update correctly, I added the equations for the velocity to the ball move function.  Accelleration vector at the moment is currently (0, 0, -9.81) to account for gravity.  Initial velocity, or V0, when entering the move function is (0, 0, 20), just an arbitrary number to see whether it updates right.  Theoretically, the ball should travel upwards, then as gravity pulls it, initial velocity should slow until reaching 0, and then the ball should fall.  The ball move function, with only log entries omitted, is:
void move()
{
VF.x = (V0.x+A.x)*DT*sine(theta)*cosine(phi);
VF.y = (V0.y+A.y)*DT*sine(theta)*sine(phi);
VF.z = (V0.z+A.z)*DT*cosine(phi);
pos += VF*DT;
if(pos.z <= 0)
{
moving = false;
bounce();
}
if(C.frame == 30)
{
B.slot = env.play_2d("sounds/ball.ogg", user.x, user.y, B.pos.x, B.pos.y, false);
C.frame = 0;
}
C.tick();
C.update();
env.update_sound_2d(B.slot, B.pos.x, B.pos.y);
}
Instead of what it's supposed to do, though, the velocity just remains constant.  From test log:
Beginning move function.  Initial velocity z is 10, accelleration z is -9.8100004196167.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is -9.8100004196167.  velocity z is 0.003166659735143.
Ball is now at 0, 0, 12.0000524520874.
Beginning move function.  Initial velocity z is 10, accelleration z is -9.8100004196167.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is -9.8100004196167.  velocity z is 0.003166659735143.
Ball is now at 0, 0, 12.0001049041748.
Beginning move function.  Initial velocity z is 10, accelleration z is -9.8100004196167.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is -9.8100004196167.  velocity z is 0.003166659735143.
Ball is now at 0, 0, 12.0001573562622.
As you can see, the velocity just remains constant.  It's not supposed to do that, I don't think.  And since I put the math equations inside of this function, they should update with each iteration, correct?  What am I doing wrong here?

2019-02-13 23:42:46

It seems like you're using the equation for finding velocity after a certain amount of time has passed, but not adding it to the current velocity. If I'm reading it right, anyway, which might not be the case.
For gravity defined as a vector, I'd probably just leave it at vf += g*dt. That's probably a mistake, since there are other forces involved, but the point is still that you're finding the change, then adding that to the original. I've generally broken this into separate changes, rather than calculating all the changes and saying v += dv for the whole frame, but there's probably a reason the latter is better.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-14 00:41:42

Ok, slightly confused.  I've got two velocity vectors.  V0, or initial velocity, and VF, or final velocity.  The equation for final velocity is:
vx = vx0 + ax * t
vy = vy0 + ay * t
vz = vz0 + az * t
Which is what I did here, I think, unless somehow I didn't write it correctly in terms of code:
VF.x = (V0.x+A.x)*DT*sine(theta)*cosine(phi);
VF.y = (V0.y+A.y)*DT*sine(theta)*sine(phi);
VF.z = (V0.z+A.z)*DT*cosine(phi);
So, if V0 is (0, 0, 20) such that, for now, the ball exceeds gravity so that it should move upward, and assuming that, again, for now, gravity is the only force on the ball:
(V0.z+A.z) is equal to (20+-9.81), which should equal 10.19.  So the next time the ball move function iterates, shouldn't V0.z be 10.19, not 20?  Then the equation occurs again:
VF.z = (10.19+-9.81)… which equals 0.38, if my math is correct.  Isn't that the way this should work?  Each time the ball move function iterates, the math done on the preceeding iteration should hold, since the vector values get reassigned each time the equation runs.  Look:
VF.x = (V0.x+A.x)...
VF.y = (V0.y+A.y)...
VF.z = (V0.z+A.z)...
So each time the ball move function iterates, VF.x, y,  and z are assigned, correct?  Shouldn't that allow for the values to assign correctly?  Or am I missing something?

2019-02-14 03:05:49

Something about the arrangement of parentheses is confusing. If v.z == 20 and A.z == -9.81, (v.z+A.z)*dt gets the wrong answer, because dt is being multiplied by v.z.
I think what you want is more like (v0.z + (A.z * dt)) * cosine(phi). Actually, I'm not sure that cos is necessary, here, since that would rotate gravity.
Having said all that, if dt == 1 and phi == 0, (20-9.81)*1*cos(0) should indeed give 10.19.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-14 03:18:23 (edited by JLove 2019-02-14 03:27:40)

Ok, so I changed the code to reflect what you wrote above:
VF.z = (V0.z+(A.z*DT))...
Log shows:
Beginning move function.  Initial velocity z is 20, accelleration z is -9.8100004196167.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is -9.8100004196167.  velocity z is 19.8365001678467.
Ball is now at 0, 0, 13.3306083679199.
Beginning move function.  Initial velocity z is 20, accelleration z is -9.8100004196167.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is -9.8100004196167.  velocity z is 19.8365001678467.
Ball is now at 0, 0, 13.6612167358398.
Beginning move function.  Initial velocity z is 20, accelleration z is -9.8100004196167.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is -9.8100004196167.  velocity z is 19.8365001678467.
Ball is now at 0, 0, 13.9918251037598.
What...The hell?  Aside from the fact that the velocity still remains constant, and that V0 isn't decreasing, 19.8365001678467 is very, very wrong.  Also, DT seems weird to me.  I have FPS set to 60.

2019-02-14 04:25:51

Dt is 1/60, so that's correct.
The issue seems to be that vf needs to become the new v0. If v0.z is always 20, vf will always be 19.83[...]. Strictly speaking, v0 should be set to the ball's velocity. It doesn't matter if this is before or after vf is calculated, unless you have other calculations that rely on v0 not updating until the next frame.
So vf doesn't change because v0 doesn't change.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-14 04:41:02

But if I set V0 to VF, then doesn't that screw up the equation?  It seems like the ball would never rise like it's supposed to.  If I throw a ball upwards at an initial  velocity V0 of, say, 30, it will rise, and as gravity exerts a force on the ball, that velocity will decrease.  Once it hits 0, the ball has reached its max height, at which point it will begin to fall back to earth.  If I make VF = V0, then how will the ball be able to rise like it should?

2019-02-14 05:34:12 (edited by JLove 2019-02-14 07:30:07)

Got it, I think.  Just under the line that moves the ball:
pos += vel*DT;
I added one line of code:
V0 += G;
Where G is a gravity vector.  Test log now shows:
Beginning move function.  Initial velocity z is 20, accelleration z is 0.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is 0.  velocity z is 20.
Ball is now at 0, 0, 13.3333330154419.
Beginning move function.  Initial velocity z is 10.1899995803833, accelleration z is 0.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is 0.  velocity z is 10.1899995803833.
Ball is now at 0, 0, 13.5031661987305.
Beginning move function.  Initial velocity z is 0.379999160766602, accelleration z is 0.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is 0.  velocity z is 0.379999160766602.
Ball is now at 0, 0, 13.5094995498657.
Beginning move function.  Initial velocity z is -9.4300012588501, accelleration z is 0.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is 0.  velocity z is -9.4300012588501.
Ball is now at 0, 0, 13.3523330688477.
Beginning move function.  Initial velocity z is -19.2400016784668, accelleration z is 0.  DT is 0.016666666666666.
In move function, just past where velocity vector values are assigned, and just before the line to move the ball.  Accelleration z is 0.  velocity z is -19.2400016784668.
Ball is now at 0, 0, 13.0316667556763.
Beginning move function.  Initial velocity z is -29.0500030517578, accelleration z is 0.  DT is 0.016666666666666.
It seems to work now, although I am not sure whether actual velocity is supposed to go past -9.81 like that.  If not, I need to know so I can fix it.
UPDATE:  In experimenting with adjusting the function to throw the ball, I noticed something I did not know you could do with vectors in BGT.  I put my code back the way it was, where velocity is returned in a function:
vector SetVel()
{
VF.x = (V0.x+(A.x*DT))*sine(theta)*cosine(phi);
VF.y = (V0.y+(A.y*DT))*sine(theta)*sine(phi);
VF.z = (V0.z+(A.z*DT))*cosine(phi);
return VF;
}
I didn't know I could do this with vectors, but this works from wherever I please:
VF = SetVel();
If I had known that, updating vel anywhere wouldn't have been an issue.  Damn.  I assume I can do it with the other vectors as well.

2019-02-14 11:21:25

The reason v0 changes to vf is because you're calculations are for the time that passes from frame to frame. There probably isn't much need to remember what the initial velocity was 10 frames ago, but you do need to know what it is at the end of the previous frame.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-17 09:19:05 (edited by JLove 2019-02-17 09:20:09)

Ok, just noticed that the clock wasn't exactly working correctly.  Where do the tick and update functions need to be so that things actually update right?  Do I want the tick and update functions in my main game loop where I check for all sorts of rules, key presses and other things, or do the tick and update functions only go in places where the ball, racket, or players move, and thusly, math becomes relevant?  I assume the latter, but I want to make sure.

2019-02-17 11:03:12

If you mean the clock's tick method, I always call it in the main loop. The math needs know how much time passes between frames, but if clock::tick is called in any function or method that might be invoked more than once per frame, things will get very confusing very quickly.
If it helps, the way this was handled in Java was that the Timer class takes two parameters: the delay between frames, and the function to call every frame. In this way, you didn't even bother with a main loop, instead just starting the timer. BGT doesn't have multithreading, though, so the best I could come up with was having a {keycheck(); step(dt); clock.tick();} sort of loop.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-17 21:55:11 (edited by JLove 2019-02-17 22:59:18)

You have two methods in clock.  Tick() and update().  Update appears to reset the timer for each frame after DT is reached:
bool update()
{
if (time.elapsed>=delay) {
time.restart();
time.resume();
return true;
}
return false;
}
That would seem to suggest that wherever tick() is called, update() has to be called as well, although update is a Boolean, so not positive.  Just want to make sure that I get this correct.  I ran a check to show what the timer showed after each frame right before it was reset with FPS set to 60.  I added the test entry in the update function, just before time.restart(), and there's quite a bit of jumping around.  Ten frames:
frames = 1, and elapsed times is 34.
frames = 2, and elapsed times is 28.
frames = 3, and elapsed times is 26.
frames = 4, and elapsed times is 30.
frames = 5, and elapsed times is 27.
frames = 6, and elapsed times is 26.
frames = 7, and elapsed times is 28.
frames = 8, and elapsed times is 26.
frames = 9, and elapsed times is 25.
frames = 10, and elapsed times is 25.
Notice the large variability from frame 1 to 2, where timer goes from 34 to 28, sometimes down to 25.  Then I ran a test to see whether it indeed ran 60 frames per second.  I had my iPhone set a timer for 10 seconds, and started the game.  After 10 seconds, number of frames shows as only 284.  Last time I checked, 60 x 10 was 600, not 284.  Not sure what is wrong here.  To be thorough, I then created a separate timer to track real time and added the following lines of code to my main game loop for testing:
if(RealTime.elapsed>= 10000)
{
test.add_entry("Amount of real time that has passed is " + (RealTime.elapsed/1000.0) + "seconds.  Number of frames that have passed is " + C.frame + ".");
C.frame = 0;
TRestart(RealTime);
}
This should write the number of frames that pass each time ten seconds pass.  I have FPS set to 60, so 600 frames should pass.  This is what log shows:
Amount of real time that has passed is 10.005seconds.  Number of frames that have passed is 301.
Amount of real time that has passed is 10.01seconds.  Number of frames that have passed is 301.
Amount of real time that has passed is 10.011seconds.  Number of frames that have passed is 302.
Amount of real time that has passed is 10.031seconds.  Number of frames that have passed is 302.
Amount of real time that has passed is 10.001seconds.  Number of frames that have passed is 301.
Amount of real time that has passed is 10.008seconds.  Number of frames that have passed is 303.
What the hell?  I double checked, and FPS is set to 60.  Aside from there being about half of the frames that there should be, some of these numbers make no sense.  For example:
Amount of real time that has passed is 10.008seconds.  Number of frames that have passed is 303.
Ok, but then we've got:
Amount of real time that has passed is 10.031seconds.  Number of frames that have passed is 302.
And at 10.01, we've got 301.  So if 10.031 shows 302, 10.001 shows 301, 10.01 also shows 301, 10.005 also shows 301, then how in the ever-loving realm of fuck does 10.008, which is less than 10.031, less than 10.01, and only slightly above 10.005, show 303?  That makes no damn sense.  And I thought this was going to help with the initial problem I was having that began this whole thing, where the ball was landing at different coordinates on different machines.  If my machine thinks that 303 frames pass after 10.008 seconds, and your machine thinks that 301 frames pass after 10.01 seconds, then we're still gonna have the same issue, since my machine would have passed two more frames than yours, right?  So does this mean I'm still going to have the same damn problem after all of the work I've completed?  And why in the hell are only half of the frames there?

2019-02-17 23:32:08

Update checks to see if a frame has passed, whereas tick waits however long is left until the next frame.
If you're consistently getting ticks taking in the neighborhood of twice as long as they should, something's screwy somewhere, and I'd probably stick timers and logs all over the place to try and find it.
I ran into a terrible case of this in, like, April or June 2017. I discovered that I had a huge object mortality rate (hence all the commented out destructors in 2d), and after a great deal of whittling it down to a sane amount... the lag was still atrocious. I eventually concluded my computer was just being a jerk, after optimizing shape usage, AI, etc barely put a dent in it.
As for how this affects networking, yes, the players need to have the same things happening on the same frames, or as close to this as possible. There are various strategies used to try and achieve synchronicity, because even with consoles that can more reliably prevent the games from lagging differently, they can't prevent network lag. This can get kinda weird, with things like rollbacks and frame-dropping. And let's not forget how, just the other day, Swamp had an issue where zombies were being drawn toward (0, 0) because of connections so poor that they were confusing the server.
It really doesn't seem like anything should be causing what you have to run at what is effectively half speed, though. That's going to be ... interesting, to diagnose.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-18 00:10:26 (edited by JLove 2019-02-18 02:33:00)

I wouldn't even begin to know where to start looking.  Lot of info in this post, so please read in its entirety.  For the life of me, I still  don't understand why I get this error.  If I declare DT globally, like this:
double DT = 0.001*C.delay;
BGT compiler says:
Line: double DT = 0.001*C.delay;
Error: 'delay' is not a member of 'clock'
But actually, it is most assuredly a member of clock.  However, if I add a function to return DT, it works fine:
double SetFrameRate()
{
DT = 0.001*C.delay;
return DT;
}
And it works fine.  What the hell?  That makes no sense.  In an attempt to start troubleshooting, I created a very simple BGT script to test framerate, hence the related error above.  Code is:
#include "clock.bgt"
#include "voice.bgt"
#include "logger.bgt"

bool testing = false;
logger log;
Voice speaker;
timer RT;
clock C(60);
double DT;

void main()
{
init();
RT.pause();
show_game_window("");
RT.restart();
testing = true;
while(true)
{
C.tick();
if(RT.elapsed>= 10000)
{
log.write("TimeTest.log", true);
log.add_entry("Amount of real time that has passed is " + (RT.elapsed/1000.0) + "seconds.  DT is " + DT + ".  Number of frames that have passed is " + C.frame + ".");
C.frame = 0;
RT.restart();
}
//key presses just to see current values.
if(key_pressed(KEY_E))
speaker.speak(RT.elapsed);
if(key_pressed(KEY_F))
speaker.speak(C.frame);
if(key_pressed(KEY_ESCAPE))
{
testing = false;
exit();
}
}
}
//Function to return DT, since I get that error if I don't.
double init()
{
DT = 0.001*C.delay;
return DT;
}
log shows:
Amount of real time that has passed is 10.001seconds.  DT is 0.016666666666666.  Number of frames that have passed is 610.
Amount of real time that has passed is 10.012seconds.  DT is 0.016666666666666.  Number of frames that have passed is 608.
Amount of real time that has passed is 10.029seconds.  DT is 0.016666666666666.  Number of frames that have passed is 606.
Amount of real time that has passed is 10.029seconds.  DT is 0.016666666666666.  Number of frames that have passed is 604.
Ok, so that presents a few questions.  First, how is the number of frames at 610 after 10.001 seconds?  How in the hell did ten frames pass in a thousandth of a second?  Then, even weirder, we have this:
Amount of real time that has passed is 10.029seconds.  DT is 0.016666666666666.  Number of frames that have passed is 606.
The hell?  That's more time than 10.001 is, yet there are fewer frames.  That makes no sense at all.  Then, even stranger, on the next line we have the exact same amount of time, down to the thousandth of a second, and still fewer frames, at 604.  Then, even less time passes above that, but more frames:
Amount of real time that has passed is 10.012seconds.  DT is 0.016666666666666.  Number of frames that have passed is 608.
What the fuck?  10.029 yields first 606 frames.  Then on the very next iteration of exactly 10.029 we get 604 frames, while here, 10.012, less time, yields 608, and the kicker, 10.001, the least amount of time of all of them, gives 610?  I don't get this at all.  What in bloody hell is going on?  NOTE:  Interestingly, if I add C.update() to the code just below C.tick(), it cuts the amount of frames in half for some reason, so around 300 for 10 seconds.  So not sure how to use update correctly, since cutting number of frames is not what I want to accomplish.
UPDATE:  Given what I found out about the update function cutting the frame rate in half, I removed the update() calls from everywhere in my game code, and now log shows:
Amount of real time that has passed is 10.01seconds.  DT is 0.016666666666666.  Number of frames that have passed is 514.
Amount of real time that has passed is 10.003seconds.  DT is 0.016666666666666.  Number of frames that have passed is 513.
Amount of real time that has passed is 10.002seconds.  DT is 0.016666666666666.  Number of frames that have passed is 513.
So still not where it should be.

2019-02-18 10:57:16 (edited by CAE_Jones 2019-02-18 17:47:04)

I'm guessing that declaring dt globally doesn't work because of something to do with the order of how classes and their properties are initialized under the hood. Probably the same reason a global keyconfig crashes but a handle doesn't.
I can imagine random computer shenanigans causing enough lag to reduce the effective framerate to 50, but I am just as lost as to where the extra frames come from.
If the wait function rounds to the nearest millisecond, or truncates anything past the decimal, then it's going to give different results than we'd expect. However, your first test was close enough that I'm not sure how much that could explain it.
One thing I found when I made the clock class is that there was consistently a millisecond of difference between what was reported by timers and what was sent to wait, even when there were no other sources of lag. Because of this, the tick method tries to compensate, which could account for some of the discrepency.
I copied the code you provided and ran it, and I got similar results with 2-10 extra frames. Thinking it might be a rounding issue, I tried it again at 50fps (dt = 0.02). There were a few huge lag spikes in the log (10.1 to 10.6 seconds of realtime, instead of 10 + a couple centiseconds), presumably because I was reading the previous log at the time to try and get an idea of where the discrepency was coming from. The results never went higher than 502, but sometimes got as low as 495. The low values can be explained by lag. I can account for one of the extra frames due to the tick coming before the log, but I'm less sure about the other.
I should also mention that 502 frames was the most common result, with all the others being 495-499, presumably due to random lag. I should also note that the amount of lag that made it into the realtime results has no correlation with the frame count, since the offending lag has to come in the final frame to show up in the realtime result.
Even if the 1ms compensation and truncating aren't to blame for the high results for 60fps, it's worth noting that there will be rounding errors regardless, because floating point arithmetic is imprecise for most fractions. It still seems odd that this would result in extra frames, rather than fewer.
Simply put, without being able to rely on all players having predictable amounts of lag, there will always be some small discrepencies. Given the same initial state, you'll have the same final state, even if one machine takes a fraction of a second longer to get there. The problem comes when those discrepencies add up, and it becomes necessary to push the players back toward synchronicity. Sending the frame count along with player input might help, but it isn't going to solve the problem by itself.
The discrepencies you're finding in frame count, though, are mostly due to things beyond our control, like floating point limitations and whatever Windows is doing and the state of the hardware, etc. There will be lag over the network, too, which is probably going to be far more troublesome.

I've never had a solid test case for the most common solutions to the lag-induced client discrepency issue, so I can't promise what the best way to deal with it is. If you force the clients to agree after every network event, there's a good chance this will result in jank and other such glitches. If the lag is extreme enough, that might happen anyway (I mean, it happens in mainstream multiplayer games), but can probably be minimized.

[edit] I think I've figured out the other extra frame in my 50fps test. The realtime timer restarts before the loop, but the clock's timer does not, so any delay between the creation of the clock and starting the loop counts toward the first frame. So 502 frames in 10seconds makes sense. But you really shouldn't be getting more than 2 extra frames at any framerate, so I think it's probably mostly floating point imprecision for certain numbers. If it's waiting 16ms instead of 16.6[...], the maximum frame count in 10s should be 627. The effective dt for 610 is 16.3934ms, but since we have established two frames due to the timers doing different things, it's more like 10000/608 = 16.447ms. I'm not sure what causes that, but given how frustrating lag can be when playing, and how few the extra frames are (not even up to 61fps), it's probably an error on the side of convenience, what with not effecting the need to account for lag either way.[/edit]

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.