Picking up the pace

Today I’m looking at adding acceleration (another vector!) to my code, essentially it works like this:

Our location vector is our current point in space.
Our Velocity vector is our change in location over time.
Our Acceleration vector is our change in velocity over time.

The reason we were having issues with strangeness when we changed the velocity earlier was because we are reversing the velocity when we meet a wall. When you add a positive value (our new velocity) to a negative value (our current velocity), you get a smaller negative value, resulting in a deceleration, instead of an acceleration. What we actually need to do to get a directionless change in velocity is to change the magnitude of the current velocity vector via out setMag() function.

I  playing around with this I discovered a bug in the vec2 class that needed to be corrected:

What happens if we normalize() a zeroed vector?
What happens if we setMag() a zeroed vector?

These are actually the same question, as normalize() requires that we divide the vector components by the magnitude and setMag() requires that we normalize() first!

To correct this, I added a div() function that ignores divide-by-zero cases and also modified the normalize() and setMag() functions to handle zero cases:

[pastacode lang=”cpp” manual=”void%20vec2%3A%3Adiv(int%20div)%0A%7B%0A%20%20%20%20if%20(div%20!%3D%200)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20this-%3Ex%20%2F%3D%20div%3B%0A%20%20%20%20%20%20%20%20this-%3Ey%20%2F%3D%20div%3B%0A%20%20%20%20%7D%0A%7D%0A%0Avoid%20vec2%3A%3Anormalize()%0A%7B%0A%20%20%20%20float%20mag%20%3D%20this-%3EgetMag()%3B%0A%20%20%20%20if%20(mag)%0A%20%20%20%20%20%20%20%20div(mag)%3B%0A%7D%0A%0Avoid%20vec2%3A%3AsetMag(float%20mag)%0A%7B%0A%20%20%20%20if(this-%3EgetMag()%20!%3D%200)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20this-%3Enormalize()%3B%0A%20%20%20%20%20%20%20%20this-%3Emult(mag)%3B%0A%20%20%20%20%7D%0A%20%20%20%20else%20if(%20mag%20%3E%200%20)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20this-%3Ex%20%3D%201%20*%20mag%3B%0A%20%20%20%20%20%20%20%20this-%3Ey%20%3D%201%20*%20mag%3B%0A%20%20%20%20%7D%0A%7D” message=”” highlight=”” provider=”manual”/]

 

Now with this modified code we can increase and decrease the velocity of the bouncing image with the following calls in our event handler:

[pastacode lang=”cpp” manual=”case%20SDLK_a%3A%0A%20%20%20%20velocity.setMag(velocity.getMag()%20%2B%201)%3B%0A%20%20%20%20break%3B%0A%0Acase%20SDLK_z%3A%0A%20%20%20%20velocity.setMag(velocity.getMag()%20-%201)%3B%0A%20%20%20%20break%3B” message=”” highlight=”” provider=”manual”/]

There is no special error handling for zero cases as it’s all handled within the class!

 


 

In order to clean main a little bit, I decided I wanted to break the SDL initialization functions into there own separate fucntion, this turned out to be quite a confusing rabbit hole where I ended up learning about pointers to pointers and references to pointers.

The thing to know about pointers is that when you pass a pointer to a function, the pointer will be dereferenced and ultimately you are effectively passing a copy of the value that the pointer points to, not the pointer itself, this doesn’t work if you want to actually change the contents of the pointer within a function.

 

Here’s the code in question:

[pastacode lang=”cpp” manual=”bool%20initializeSDL(SDL_Window*%26%20win%2C%20SDL_Renderer*%26%20ren)%0A%7B%0A%20%20%20%20if%20(SDL_Init(SDL_INIT_VIDEO)%20!%3D0)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20logSDLError(std%3A%3Acout%2C%20%22SDL%20Init%22)%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20win%20%3D%20SDL_CreateWindow(%22Hello%20World%22%2C%20100%2C%20100%2C%20640%2C%20480%2C%20SDL_WINDOW_SHOWN)%3B%0A%20%20%20%20if%20(win%20%3D%3D%20nullptr)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20logSDLError(std%3A%3Acout%2C%20%22SDL_CreateWindow%22)%3B%0A%20%20%20%20%20%20%20%20SDL_Quit()%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20ren%20%3D%20SDL_CreateRenderer(win%2C%20-1%2C%20SDL_RENDERER_ACCELERATED%20%7C%20SDL_RENDERER_PRESENTVSYNC)%3B%0A%20%20%20%20if%20(ren%20%3D%3D%20nullptr)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20SDL_DestroyWindow(win)%3B%0A%20%20%20%20%20%20%20%20logSDLError(std%3A%3Acout%2C%20%22SDL_CreateRenderer%22)%3B%0A%20%20%20%20%20%20%20%20SDL_Quit()%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(%20(IMG_Init(IMG_INIT_PNG)%20%26%20IMG_INIT_PNG)%20!%3D%20IMG_INIT_PNG)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20logSDLError(std%3A%3Acout%2C%20%22IMG_init%22)%3B%0A%20%20%20%20%20%20%20%20SDL_Quit()%3B%0A%20%20%20%20%20%20%20%20return%201%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20true%3B%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

We would first initialize our window and renderer pointers to nullptr, and call this function with: initializeSDL(window, renderer).

This would normally pass the copy of the data to the function, but because we specified in the function that we want a reference to a pointer(*&), it will instead pass the pointer itself without dereferencing it, this allows us to manipulate the pointer itself, in this case, filling our previously created pointers with an SDL_Window and an SDL_Renderer respectively.

 


Acceleration

Finally, we’re getting to acceleration, what was intended to be the focal point of this post!

We’ll start by just adding a constant acceleration. As before, this has the strange effect of having the bouncing image work it’s way down to the bottom right corner due to the inverting velocity, we can more easily observe what is happening by wrapping the object instead of bouncing it:

[pastacode lang=”cpp” manual=”%2F%2Fif%20(%20(position.getX()%20%3E%3D%20(SCREEN_WIDTH%20-%20iW)%20)%20%7C%20(position.getX()%20%3C%3D%200))%0A%2F%2F%20%20%20%20velocity.setX(velocity.getX()%20*-1)%3B%0A%2F%2Fif%20(%20(position.getY()%20%3E%3D%20(SCREEN_HEIGHT%20-%20iH)%20)%7C%20(position.getY()%20%3C%3D%200))%0A%2F%2F%20%20%20%20velocity.setY(velocity.getY()%20*-1)%3B%0A%0Aif%20(position.getX()%20%3E%20SCREEN_WIDTH)%0A%20%20%20%20position.setX(0)%3B%0Aelse%20if(position.getX()%20%3C%200)%0A%20%20%20%20position.setX(SCREEN_WIDTH)%3B%0A%0Aif%20(position.getY()%20%3E%20SCREEN_HEIGHT)%0A%20%20%20%20position.setY(0)%3B%0Aelse%20if%20(position.getY()%20%3C%200)%0A%20%20%20%20position.setY(SCREEN_HEIGHT)%3B” message=”” highlight=”” provider=”manual”/]

 

The object quickly accelerates to an insane pace, it should be limited, so we’ll add a limit() function to the vec2 class:

[pastacode lang=”cpp” manual=”void%20vec2%3A%3Alimit(float%20limit)%0A%7B%0A%20%20%20%20if%20(this-%3EgetMag()%20%3E%20limit)%0A%20%20%20%20%20%20%20%20this-%3EsetMag(limit)%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

This just checks the current magnitude, compares it to limit and if it’s higher, sets it equal to limit.

 

This isn’t terribly useful in and of itself, but if we accelerate based on a more dynamic vector, like the mouse location, we can get some more interesting behaviour:

[pastacode lang=”cpp” manual=”vec2%20acceleration%20%3D%20vec2%3A%3Asub(mouse%2C%20position)%3B%0Aacceleration.normalize()%3B%0Aacceleration.mult(.5)%3B%0A%0Avelocity.add(acceleration)%3B%0Aposition.add(velocity)%3B%0Avelocity.limit(10)%3B” message=”” highlight=”” provider=”manual”/]

So here we had to make some changes to our vec2 class to make it more useful:

We added two static functions: one for subtraction and one for addition and we changed all instances of int to float for greater precision.

The interesting thing here are the static functions; declaring a function as static allows you to call that function from anywhere without needing to create an object in order to use it. For example, in the code snippet above we have vec2 acceleration = vec2::sub(mouse, position), this creates a vec2 object called acceleration and makes it’s values equal to mouseposition which is a vector pointing from the object position to the mouse location and with a magnitude equal to the distance between them.

After normalizing and scaling, we have an acceleration vector that will cause the image object to accelerate toward the mouse cursor at all times!

 

Whew! That was a lot to get through today, hopefully tomorrow we can start looking into applying forces on demand instead of constant accelerations as well as making this a bit more object oriented so we can do a few more interesting things! I should also be making these changes in separate branches and then merging to master for branching practice in git.

Leave a Reply

Your email address will not be published.