Problem: I am attempting to update time using getTickCount without avail.
Situation: I currently obtain the timedifference using getTickCount and pass it into world update method parameters. However within the update method where I update the position, a large value is passed in (even though I have divided by 1000) and so the position adds an odd 4000 to the position vector.
Code below:
Simulation.CPP:
int Simulation::simControlLogic(HWND hWnd, keyEvent event)
{
/* TO DO: add relevant code */
if (event != QUIT)
{
previousTime = 0;
frameStartTime = GetTickCount();
if (previousTime == 0)
previousTime = frameStartTime;
timeDifference = (frameStartTime - previousTime) / 1000.0f; // this generates the difference between the last and current time
world.update(event, &graphics, timeDifference); // update parameters of virtual world
gameLoopDelay(frameStartTime);
simDisplayFrame(hWnd); // display frame
previousTime = frameStartTime; //the current time is set to the previous time for the next loop step
}
return 1;
}
looking into the world Update method:
int WorldData::update(keyEvent kEvent, GraphicsM * pGraphicsModule, float timeStep)
{
//updates the particle
for (int i = 0; i < 3; i++)
{
particles[i].Update(kEvent, pGraphicsModule, timeStep);
}
return 1;
}
looking into particles Update method:
void ParticleModel::Update(keyEvent kEvent, GraphicsM * pGraphicsModule, float timeStep)
{
move(timeStep);
}
looking into move method:
void ParticleModel::move(float timeStep)
{
velocity.y = 0.5F;
velocity.x = 0.5F;
acceleration.x = 0.0F;
acceleration.y = 0.0F;
pos.x += velocity.x * timeStep; //here is the problem. I get a large value e.g 79637.1788 causing pos.x to be ridiculously large
pos.y += velocity.y * timeStep; //here is the problem. I get a large value e.g 79637.1788 causing pos.y to be ridiculously large
}
After moving previousTime to the simulator constructor initializing it to 0, it worked. Thanks for replies
Related
I am making a physics-based where balls (gdi+ ellipses) will collide. There are 4 conditions I need to program to make the collisions work correctly. To do this I'm using if statements.
I noticed when debugging that in some cases, the code inside the if block is NOT executed as if the condition was not met - however - changing the code inside that if block will still alter the way my program works.
I've extensively debugged to ensure this is what's happening. I've also switched up the way I'm writing my if statements to ensure the logic is correct, but the problem persists.
When the game is started, I add a couple of balls to a vector.
/**
* Start a new game
*/
void CGame::NewGame()
{
this->mBalls.clear();
shared_ptr<CBall> cueBall = make_shared<CBall>(this);
cueBall->SetX(410.0);
cueBall->SetY(379.0 - (25 / 2) - 110);
cueBall->SetVelocityX(0);
cueBall->SetVelocityY(160.0);
this->mGameItems.push_back(cueBall);
shared_ptr<CBall> ball1 = make_shared<CBall>(this);
ball1->SetX(420.0);
ball1->SetY(379.0 - (25 / 2));
this->mGameItems.push_back(ball1);
}
They are then drawn on the screen by iterating through the vector CGame.mGameItems and calling a Draw function on each one.
/**
* Draw the ball with as a circle
* \param graphics The GDI+ graphics context to draw on
*/
void CBall::Draw(Gdiplus::Graphics* graphics)
{
SolidBrush colorBrush(Color(255, 1000, 1000, 1000));
graphics->FillEllipse(&colorBrush, Rect(this->GetX(), this->GetY(), this->diameter, this->diameter));
}
The redrawing and updating of the window is done in CChildView::OnPaint.
/** This function is called to draw in the window.
*
* This function is called in response to a drawing message
* whenever we need to redraw the window on the screen.
* It is responsible for painting the window.
*/
void CChildView::OnPaint()
{
CPaintDC paintDC(this); // device context for painting
CDoubleBufferDC dc(&paintDC); // device context for painting
Graphics graphics(dc.m_hDC); // Create GDI+ graphics context
mGame.OnDraw(&graphics);
// TODO: Add your message handler code here
// Do not call CWnd::OnPaint() for painting messages
if (mFirstDraw)
{
mFirstDraw = false;
SetTimer(1, FrameDuration, nullptr);
/*
* Initialize the elapsed time system
*/
LARGE_INTEGER time, freq;
QueryPerformanceCounter(&time);
QueryPerformanceFrequency(&freq);
mLastTime = time.QuadPart;
mTimeFreq = double(freq.QuadPart);
}
/*
* Compute the elapsed time since the last draw
*/
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
long long diff = time.QuadPart - mLastTime;
double elapsed = double(diff) / mTimeFreq;
mLastTime = time.QuadPart;
mGame.Update(elapsed);
}
To handle collisions of the objects, a function calls a function called Update() on a CGame object which uses the following code to check if a collision happened.
/** Handle updates for animation
* \param elapsed The time since the last update
*/
void CGame::Update(double elapsed)
{
// Collisions of balls
for (shared_ptr<CBall> ball1 : this->GetBalls())
{
for (shared_ptr<CBall> ball2 : this->GetBalls())
{
double centerXBall1 = ball1->GetX() + ball1->GetDiameter() / 2;
double centerYBall1 = ball1->GetY() + ball1->GetDiameter() / 2;
double centerXBall2 = ball2->GetX() + ball2->GetDiameter() / 2;
double centerYBall2 = ball2->GetY() + ball2->GetDiameter() / 2;
double distance = sqrt((pow((centerXBall2 - centerXBall1), 2) +
pow((centerYBall2 - centerYBall1), 2)));
if (ball1 != ball2 && (distance <= ball1->GetDiameter() || distance <= ball2->GetDiameter()))
{
// The slope of the line that passes through the center of both
// balls
double slopeThroughCenters =
(centerYBall2 - centerYBall1) / (centerXBall2 -
centerXBall1);
// The angle between a vertical and the deflection direction of
// ball 1
double angleOfVelocity1 = 0.0;
// The angle between a horizontal line and the deflection
// direction of ball 2
double angleOfVelocity2 = 0.0;
double complimentaryAngle = atan(slopeThroughCenters);
if (centerXBall1 > centerXBall2 && centerYBall1 > centerYBall2)
{
angleOfVelocity1 = PI / 2 - complimentaryAngle;
angleOfVelocity2 = (PI / 2 - complimentaryAngle) + PI / 2;
}
double initialVelocityBall1 = sqrt(pow(ball1->GetVelocityX(), 2) +
pow(ball1->GetVelocityY(), 2));
double initialVelocityBall2 = sqrt(pow(ball2->GetVelocityX(), 2) +
pow(ball2->GetVelocityY(), 2));
double finalVelocityBall1 = ((ball1->GetMass() - ball2->GetMass()) * initialVelocityBall1) /
(ball1->GetMass() + ball2->GetMass());
double finalVelocityBall2 = (2 * ball1->GetMass() * initialVelocityBall1) /
(ball1->GetMass() + ball2->GetMass());
double velocityXBall1 = finalVelocityBall2 * cos(angleOfVelocity1);
double velocityYBall1 = finalVelocityBall2 * sin(angleOfVelocity1);
double velocityXBall2 = finalVelocityBall2 * cos(angleOfVelocity2);
double velocityYBall2 = finalVelocityBall2 * sin(angleOfVelocity2);
ball1->SetVelocityX(velocityXBall1);
ball1->SetVelocityY(velocityYBall1);
ball2->SetVelocityX(velocityXBall2);
ball2->SetVelocityY(velocityYBall2);
}
}
}
for (auto ball : this->mBalls)
{
ball->Update(elapsed);
}
}
When I run the debugger, the if block:
if (centerXBall1 > centerXBall2 && centerYBall1 > centerYBall2)
{
angleOfVelocity1 = PI / 2 - complimentaryAngle;
angleOfVelocity2 = PI - complimentaryAngle;
}
Doesn't seem to be executed, however, if I change the variable's values, for example,
angleOfVelocity1 = PI;
angleOfVelocity2 = PI;
My program actually changes according to the values of those variables. How is this possible if the debugger shows that the if block isn't executed in the first place?
Sorry if my code isn't minimalistic and concise enough, but I did not want to leave out anything important for solving this problem.
I wanted to implement a special Game loop where the game actually only updates 25 frames a second while it is rendered at the top speed of the computer. I followed the article from dewitters game loop and have the interpolation setup correctly i believe (btw i'm using sdl2)...
const int TICKS_PER_SECOND = 25;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 5;
Uint32 next_game_tick = SDL_GetTicks();
int loops;
float interpolation;
while (running)
{
loops = 0;
while (SDL_GetTicks() > next_game_tick && loops < MAX_FRAMESKIP)
{
Update();
next_game_tick += SKIP_TICKS;
loops++;
}
interpolation = float(SDL_GetTicks() + SKIP_TICKS - next_game_tick) / float(SKIP_TICKS);
Render(interpolation);
}
But I don't really understand how to implement the interpolation within the render call. i tried just setting the x and y position of my sprite relative to the interpolation...
interPos.x = pos.x + int (speed * interpolation);
interPos.y = pos.y + int (speed * interpolation);
link.Draw(ren, interPos, 0, false);
but this just made the main character sprite jitter all around. Any help appreciated!
I am trying to get my tokens on a board game to fall slowly. Right now, they fall, but they fall so fast. How could I implement the timer function in my code? Right now I do a loop, that updates the y coordinate of glTranslate. But it is still too fast! the top y is the y coordinate where I press on the screen, and the bottomy is the coordinates of the lowest open spot for a token.
col =0;
double bottomy = 0;
int row = 0;
circlex = (double)x / width ;
circley = (double)y / height ;
row = board.getRow(col) + 1;
bottomy = 500 - (25*row);
for( double topy = y ; topy <= bottomy; topy += 2 ){
glTranslatef(circlex, circley, 0.0f);
circley += .0000000000000000001;
display();
}
r = board.makeMove(col);
You can use glutTimerFunc to execute a function at a regular time period. This has the signature
void glutTimerFunc(unsigned int msecs,
void (*func)(int value),
value);
For example if your drawing function was
void UpdateTokens(int time);
Then you could call an update every 0.5 seconds with the following call (where current_time was the current simulation time)
glutTimerFunc(500, UpdateTokens, current_time);
For more precise timing, I would recommend using <chrono> instead, and performing your timing using things like std::chrono::duration with a std::chrono::steady_clock.
The actual problem here is how glut works. Basically, the user only gets a image presented at the end of the main loop. As long as you do not return from the mouse function, nothing is presented on screen. You can solve the problem by transferring the work to the display function and distribute the translation across multiple frames:
global variables:
double circlex = 0, circley = 0, bottomy = 0;
bool isfalling = false;
int topy = 0;
mouse_func:
if (isfalling == false) //Prevents the user from clicking during an animation
{
circlex = (double)x / width ;
circley = (double)y / height ;
int row = board.getRow(col) + 1;
bottomy = 500 - (25*row);
topy = y;
isfalling = true;
}
display_func:
if (isfalling)
{
circley += .0000000000000000001;
topy += 2;
if (topy >= bottomy)
isfalling = false;
}
glTranslatef(circlex, circley, 0.0f);
display();
I'm making a 2D game with SFML in C++ and I have a problem with collision. I have a player and a map made of tiles. Thing that doesn't work is that my collision detection is not accurate. When I move player up and then down towards tiles, it ends up differently.
I am aware that source of this problem may be calculating player movement with use of delta time between frames - so it is not constant. But it smooths movement, so I don't know how to do it other way. I tried with constant speed valuses and to make collision fully accurate - speed had to be very low and I am not satisfied with that.
void Player::move() {
sf::Vector2f offsetVec;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
offsetVec += sf::Vector2f(0, -10);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
offsetVec += sf::Vector2f(0, 10);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
offsetVec += sf::Vector2f(-10, 0);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
offsetVec += sf::Vector2f(10, 0);
this->moveVec += offsetVec;
}
void Player::update(float dt, Map *map) {
sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
this->moveVec.y * this->playerSpeed * dt);
sf::Sprite futurePos = this->sprite;
futurePos.move(offset);
if (map->isCollideable(this->pos.x, this->pos.y, futurePos.getGlobalBounds())) {
this->moveVec = sf::Vector2f(0, 0);
return;
}
this->sprite.move(offset);
this->pos += offset;
this->moveVec = sf::Vector2f(0, 0);
return;
}
In player position update I create future sprite object, which is object after applying movement, to get it's boundaries and pass it to collision checker. To collision checker I also pass player pos, because my map is stored in 2d array of tile pointers, so I check only these in player range.
bool Map::isCollideable(float x, float y, const sf::FloatRect &playerBounds) {
int startX = int(x) / Storage::tileSize;
int startY = int(y) / Storage::tileSize;
Tile *tile;
for (int i = startX - 10; i <= startX + 10; ++i) {
for (int j = startY - 10; j <= startY + 10; ++j) {
if (i >= 0 && j >= 0) {
tile = getTile(i, j);
if (tile != nullptr && playerBounds.intersects(tile->getGlobalBounds()))
return true;
}
}
}
return false;
}
Full project on Github
My solution
I have changed if statement in update function to while statement, which decreases my offset vector till no collision is present. I still have to make some adjustments, but general idea is:
void Player::update(float dt, Map *map) {
int repeats = 0;
sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
this->moveVec.y * this->playerSpeed * dt);
sf::Sprite futurePos = this->sprite;
while (map->isCollideable(this->pos.x, this->pos.y, futurePos, offset)) {
offset = 0.7f * offset;
repeats++;
if (repeats > 5) {
this->moveVec = sf::Vector2f(0, 0);
return;
}
}
this->sprite.move(offset);
this->pos += offset;
this->moveVec = sf::Vector2f(0, 0);
return;
}
I also had to rework isCollideable method a little, so it accepts sf::Sprite and offset vector so it can calculate boundaries on it's own.
When the player collides with a tile, you should calculate the penetration, that is, the value of "how much the player went into the tile". When you have this value, nudge your player back that much.
This is just a thought but you could have some inaccuracies in your collision detection when you typecast the float x, and y to integers and then divide them. This could cause problems because some of the data in the float could be lost. If the float was 3.5 or 3.3 or 3.9 then it would become 3 which throws off your collision calculations.
The following is my timestep:
void step(float dt){
static double UPDATE_INTERVAL = 1.0f/60.0f;
static double MAX_CYCLES_PER_FRAME = 5;
static double timeAccumulator = 0;
timeAccumulator += dt;
if (timeAccumulator > (MAX_CYCLES_PER_FRAME * UPDATE_INTERVAL))
{
timeAccumulator = UPDATE_INTERVAL;
}
int32 velocityIterations = 3;
int32 positionIterations = 2;
while (timeAccumulator >= UPDATE_INTERVAL)
{
timeAccumulator -= UPDATE_INTERVAL;
this->world->Step(UPDATE_INTERVAL,
velocityIterations, positionIterations);
this->world->ClearForces();
}
}
While the game as such works great regardless of the framerate, it causes sprites to "tremble", or "stutter" even when the framerate is 60fps!
I think this is because each frame the sprite moves by a different amount, because each frame the number of times the while loop executes is different.
What is a better way to fix the time step ? I have read a number of articles on fixing the time step, and am very confused. The above "works" except for the stutter.
EDIT: Just to clarify, the tremble is very small! People with poor eyesight don't notice it, but it makes a game look very low budget if you look carefully. It makes the game look not smooth.
you don't clamp your timeAccumulator correctly:
timeAccumulator += dt;
if (timeAccumulator > (MAX_CYCLES_PER_FRAME * UPDATE_INTERVAL))
{
timeAccumulator = UPDATE_INTERVAL;
}
should be
timeAccumulator = std::min(dt + timeAccumulator, MAX_CYCLES_PER_FRAME * UPDATE_INTERVAL);
Concerning the stepping: you'll need to interpolate the last full step with the next full step according to the left-over in timeAccumulator. There was a tutorial on this by Allan Bishop though the original site is no longer reachable (blog.allanbishop.com). Here's how his smooth step (only that last one with the fractional step) looks like:
// for the remaining timeAccumulator < UPDATE_INTERVAL
fixedTimestepAccumulatorRatio = timeAccumulator / UPDATE_INTERVAL;
private function smoothStates():void
{
const oneMinusRatio:Number = 1.0 - fixedTimestepAccumulatorRatio;
//Makes it easier to wrap up the Box2D body in another object so we can track other variables.
for each (var body:Body in bodies)
{
var box2Dbody = body.box2DBody;
texture.position.x = fixedTimestepAccumulatorRatio * box2Dbody.GetPosition().x + oneMinusRatio * body.previousPosition.x;
texture.position.y = fixedTimestepAccumulatorRatio * box2Dbody.GetPosition().y + oneMinusRatio * body.previousPosition.y;
texture.rotation = box2Dbody.GetAngle() * fixedTimestepAccumulatorRatio + oneMinusRatio * body.previousAngle;
}
}