Visualize motion/gesture from acceleration data - c++

I have implemented a gesture detection algorithm where a user can define his own gestures. The gestures are defined by acceleration values send from an accelerometer.
Now my question is: Is it possible to visualize the performed gesture, so that the user can identify what gesture he performed?
My first idea and try was just to use Verlet Velocity Integration (as describec here: http://lolengine.net/blog/2011/12/14/understanding-motion-in-games) to calculate the corresponding positions and use those to form a line strip in OpenGL. The rendering works, but the result is not at all what the performed gesture looked like.
This is my code:
float deltaTime = 0.01;
PosData null;
null.x = 0.0f;
null.y = 0.0f;
null.z = 0.0f;
this->vertices.push_back(null);
float velX = 0.0f;
float velY = 0.0f;
float velZ = 0.0f;
for (int i = 0; i < accData.size(); i++) {
float oldVelX = velX;
float oldVelY = velY;
float oldVelZ = velZ;
velX = velX + accData[i].x * deltaTime;
velY = velY + accData[i].y * deltaTime;
velZ = velZ + accData[i].z * deltaTime;
PosData newPos;
newPos.x = vertices[i].x + (oldVelX + velX) * 0.5 * deltaTime;
newPos.y = vertices[i].y + (oldVelY + velY) * 0.5 * deltaTime;
newPos.z = vertices[i].z + (oldVelZ + velZ) * 0.5 * deltaTime;
this->vertices.push_back(newPos);
}
I am using a deltaTime of 0.01 because my accelerometer sends the acceleration data every 10 milliseconds.
Now i am wondering: Is there something wrong with my calculation? Could it even work this way? Is there a library which can do this? Any other suggestions?

as the theoretical physics and monte-carlo-simulation man, I've thought about the discrepancies you've observed. You wrote that the "real" gesture curve (3D) didn't resemble at all the calculatet curve. You might want to consider several aspects of the problem at hand. First, what do we know about the "real" gesture curve in space. We certainly do have "some" curve in mind, but that needn't look much like the "real" curve performed by one's hand. Second, what do we know about the quality of the accelerometer, about its accuracy, about its latency or other intricacies? Third point, what do we know about the "measured" acceleration data, do they fit "some" nice and smooth curve when drawn in x-y-plot shape? If that curve isn't really very smooth-looking, then one needs assumptions about the acceleration data to perform a linear, or better, a spline-fit. Afterwards you can integrate simply by analytical formulae. -- You might think in terms of signing electronically when the UPS parcel service asks you to, what does your signature look like on that pad? This is what probably happens with your acceleration system, without some "intelligent" curvature-smoothing algorithm. Hope my comment could be helpful in one way or another ... Regards, M.

Related

Enemies path following (Space Shooter game)

I am recently working with SFML libraries and I am trying to do a Space Shooter game from scratch. After some time working on it I get something that works fine but I am facing one issue and I do not know exactly how to proceed, so I hope your wisdom can lead me to a good solution. I will try to explain it the best I can:
Enemies following a path: currently in my game, I have enemies that can follow linear paths doing the following:
float vx = (float)m_wayPoints_v[m_wayPointsIndex_ui8].x - (float)m_pos_v.x;
float vy = (float)m_wayPoints_v[m_wayPointsIndex_ui8].y - (float)m_pos_v.y;
float len = sqrt(vx * vx + vy * vy);
//cout << len << endl;
if (len < 2.0f)
{
// Close enough, entity has arrived
//cout << "Has arrived" << endl;
m_wayPointsIndex_ui8++;
if (m_wayPointsIndex_ui8 >= m_wayPoints_v.size())
{
m_wayPointsIndex_ui8 = 0;
}
}
else
{
vx /= len;
vy /= len;
m_pos_v.x += vx * float(m_moveSpeed_ui16) * time;
m_pos_v.y += vy * float(m_moveSpeed_ui16) * time;
}
*m_wayPoints_v is a vector that basically holds the 2d points to be followed.
Related to this small piece of code, I have to say that is sometimes given me problems because getting closer to the next point becomes difficult as the higher the speed of the enemies is.
Is there any other way to be more accurate on path following independtly of the enemy speed? And also related to path following, if I would like to do an introduction of the enemies before each wave movement pattern starts (doing circles, spirals, ellipses or whatever before reaching the final point), for example:
For example, in the picture below:
The black line is the path I want a spaceship to follow before starting the IA pattern (move from left to right and from right to left) which is the red circle.
Is it done hardcoding all and each of the movements or is there any other better solution?
I hope I made myself clear on this...in case I did not, please let me know and I will give more details. Thank you very much in advance!
Way points
You need to add some additional information to the way points and the NPC's position in relationship to the way points.
The code snippet (pseudo code) shows how a set of way points can be created as a linked list. Each way point has a link and a distance to the next way point, and the total distance for this way point.
Then each step you just increase the NPC distance on the set of way points. If that distance is greater than the totalDistance at the next way point, follow the link to the next. You can use a while loop to search for the next way point so you will always be at the correct position no matter what your speed.
Once you are at the correct way point its just a matter of calculating the position the NPC is between the current and next way point.
Define a way point
class WayPoint {
public:
WayPoint(float, float);
float x, y, distanceToNext, totalDistance;
WayPoint next;
WayPoint addNext(WayPoint wp);
}
WayPoint::WayPoint(float px, float py) {
x = px; y = py;
distanceToNext = 0.0f;
totalDistance = 0.0f;
}
WayPoint WayPoint::addNext(WayPoint wp) {
next = wp;
distanceToNext = sqrt((next.x - x) * (next.x - x) + (next.y - y) * (next.y - y));
next.totalDistance = totalDistance + distanceToNext;
return wp;
}
Declaring and linking waypoints
WayPoint a(10.0f, 10.0f);
WayPoint b(100.0f, 400.0f);
WayPoint c(200.0f, 100.0f);
a.addNext(b);
b.addNext(c);
NPC follows way pointy path at any speed
WayPoint currentWayPoint = a;
NPC ship;
ship.distance += ship.speed * time;
while (ship.distance > currentWayPoint.next.totalDistance) {
currentWayPoint = currentWayPoint.next;
}
float unitDist = (ship.distance - currentWayPoint.totalDistance) / currentWayPoint.distanceToNext;
// NOTE to smooth the line following use the ease curve. See Bottom of answer
// float unitDist = sigBell((ship.distance - currentWayPoint.totalDistance) / currentWayPoint.distanceToNext);
ship.pos.x = (currentWayPoint.next.x - currentWayPoint.x) * unitDist + currentWayPoint.x;
ship.pos.y = (currentWayPoint.next.y - currentWayPoint.y) * unitDist + currentWayPoint.y;
Note you can link back to the start but be careful to check when the total distance goes back to zero in the while loop or you will end up in an infinite loop. When you pass zero recalc NPC distance as modulo of last way point totalDistance so you never travel more than one loop of way points to find the next.
eg in while loop if passing last way point
if (currentWayPoint.next.totalDistance == 0.0f) {
ship.distance = mod(ship.distance, currentWayPoint.totalDistance);
}
Smooth paths
Using the above method you can add additional information to the way points.
For example for each way point add a vector that is 90deg off the path to the next.
// 90 degh CW
offX = -(next.y - y) / distanceToNext; // Yes offX = - y
offY = (next.x - x) / distanceToNext; //
offDist = ?; // how far from the line you want to path to go
Then when you calculate the unitDist along the line between to way points you can use that unit dist to smoothly interpolate the offset
float unitDist = (ship.distance - currentWayPoint.totalDistance) / currentWayPoint.distanceToNext;
// very basic ease in and ease out or use sigBell curve
float unitOffset = unitDist < 0.5f ? (unitDist * 2.0f) * (unitDist * 2.0f) : sqrt((unitDist - 0.5f) * 2.0f);
float x = currentWayPoint.offX * currentWayPoint.offDist * unitOffset;
float y = currentWayPoint.offY * currentWayPoint.offDist * unitOffset;
ship.pos.x = (currentWayPoint.next.x - currentWayPoint.x) * unitDist + currentWayPoint.x + x;
ship.pos.y = (currentWayPoint.next.y - currentWayPoint.y) * unitDist + currentWayPoint.y + y;
Now if you add 3 way points with the first offDist a positive distance and the second a negative offDist you will get a path that does smooth curves as you show in the image.
Note that the actual speed of the NPC will change over each way point. The maths to get a constant speed using this method is too heavy to be worth the effort as for small offsets no one will notice. If your offset are too large then rethink your way point layout
Note The above method is a modification of a quadratic bezier curve where the control point is defined as an offset from center between end points
Sigmoid curve
You don't need to add the offsets as you can get some (limited) smoothing along the path by manipulating the unitDist value (See comment in first snippet)
Use the following to function convert unit values into a bell like curve sigBell and a standard ease out in curve. Use argument power to control the slopes of the curves.
float sigmoid(float unit, float power) { // power should be > 0. power 1 is straight line 2 is ease out ease in 0.5 is ease to center ease from center
float u = unit <= 0.0f ? 0.0f : (unit >= 1.0f ? 1.0f: unit); // clamp as float errors will show
float p = pow(u, power);
return p / (p + pow(1.0f - u, power));
}
float sigBell(float unit, float power) {
float u = unit < 0.5f ? unit * 2.0f : 1.0f - (unit - 0.5f) * 2.0f;
return sigmoid(u, power);
}
This doesn't answer your specific question. I'm just curious why you don't use the sfml type sf::Vector2 (or its typedefs 2i, 2u, 2f)? Seems like it would clean up some of your code maybe.
As far as the animation is concerned. You could consider loading the directions for the flight pattern you want into a stack or something. Then pop each position and move your ship to that position and render, repeat.
And if you want a sin-like flight path similar to your picture, you can find an equation similar to the flight path you like. Use desmos or something to make a cool graph that fits your need. Then iterate at w/e interval inputting each iteration into this equation, your results are your position at each iteration.
Well, I think I found one of the problems but I am not sure what the solution can be.
When using the piece of code I posted before, I found that there is a problem when reaching the destination point due to the speed value. Currently to move a space ship fluently, I need to set the speed to 200...which means that in these formulas:
m_pos_v.x += vx * float(m_moveSpeed_ui16) * time;
m_pos_v.y += vy * float(m_moveSpeed_ui16) * time;
The new position might exceed the "2.0f" tolerance so the space ship cannot find the destination point and it gets stuck because the minimum movement that can be done per frame (assuming 60fps) 200 * 1 / 60 = 3.33px. Is there any way this behavior can be avoided?

Kalman Filter for height and acceleration

im working on a stm32f417ve arm processor and trying to implement a kalman filter for fusing accelerometer data with height (pressure sensor) data.
I want to know the estimated vertical velocity and position. The accelerometer readings are rotated from body frame to earth frame, thats not the problem.
I've already searched a lot on the internet and also found some interesting things, but I'm not sure if my situation fits into the other ones i've found, so I'm here :)
This post ( Using Kalman filter with acceleration and position inputs ) is very similar to this one, but i need a little bit more help.
I've also got an MPU6000 as 6DOF imu and a MS5611 baro. I think, the best way to combine these data is to use the acceleration as a control input, am I right?
Maybe someone could look at my matrices and formulas to tell me, if its right or not.
Formulas:
//PREDICT
x = A*x + B*u
p = A*p*AT + Q
//UPDATE
Innovation = (H*p*HT + R)^-1
K = p*HT*Innovation
x = x + K*(y-H*x)
p = (I-K*H)*p
Matrizes:
#define NumState 3
#define NumInput 1
#define NumOutput 1
static float32_t xAr[NumState][1];
static float32_t uAr[NumInput][1];
static float32_t yAr[NumOutput][1];
static float32_t AAr[NumState][NumState];
static float32_t BAr[NumState][NumInput];
static float32_t HAr[NumOutput][NumState];
static float32_t QAr[NumState][NumState];
static float32_t RAr[NumOutput][NumOutput];
static float32_t PAr[NumState][NumState];
static float32_t kAr[NumState][NumOutput];
static float32_t IAr[NumState][NumState];
I put the acceleration into vector u and height into y.
The Matrix IAr is just a identity matrix, so its diagonal elements are 1.
RAr[0][0] = 0.1f;
QAr[0][0] = 1.0f;
QAr[0][1] = 1.0f;
QAr[0][2] = 0.0f;
QAr[1][0] = 1.0f;
QAr[1][1] = 1.0f;
QAr[1][2] = 0.0f;
QAr[2][0] = 0.0f;
QAr[2][1] = 0.0f;
QAr[2][2] = 0.0f;
uAr[0][0] = AccZEarth;
yAr[0][0] = Height;
HAr[0][0] = 1.0f;
HAr[0][1] = 0.0f;
HAr[0][2] = 0.0f;
BAr[0][0] = (dt*dt)/2;
BAr[1][0] = dt;
BAr[2][0] = 0.0f;
AAr[0][0] = 1.0f;
AAr[0][1] = dt;
AAr[0][2] = 0.0f - ((dt*dt)/2.0f);
AAr[1][0] = 0.0f;
AAr[1][1] = 1.0f;
AAr[1][2] = 0.0f - dt;
AAr[2][0] = 0.0f;
AAr[2][1] = 0.0f;
AAr[2][2] = 1.0f;
IAr[0][0] = 1.0f;
IAr[0][1] = 0.0f;
IAr[0][2] = 0.0f;
IAr[1][0] = 0.0f;
IAr[1][1] = 1.0f;
IAr[1][2] = 0.0f;
IAr[2][0] = 0.0f;
IAr[2][1] = 0.0f;
IAr[2][2] = 1.0f;
PAr[0][0] = 100.0f;
PAr[0][1] = 0.0f;
PAr[0][2] = 0.0f;
PAr[1][0] = 0.0f;
PAr[1][1] = 100.0f;
PAr[1][2] = 0.0f;
PAr[2][0] = 0.0f;
PAr[2][1] = 0.0f;
PAr[2][2] = 100.0f;
It would be really great if some of you guys could take a look and tell me wheter im right or wrong!
Thanks,
Chris
The first thing to determine is whether the two sensors you intend to use together are a good complement. The MEMS IMU position will diverge quickly as the double integration errors pile up. To successfully use it in this application at all you will have to calibrate its bias and scale. Those will be different on each axis, which, given your one-dimensional state, will have to be applied outside the filter. Since you are probably going to be outdoors (where an altimeter is interesting) your bias/scale calibration should also be temperature compensated.
You can easily test the IMU by doing the x = A*x + B*u loop while the IMU sits on your desk to see how quickly x[0] becomes large. Given what I know about IMUs and altimeters (not as much as IMUs) I would guess that your IMU-derived position will be worse than your raw altimeter reading within a few seconds. Much faster if the bias and scale aren't properly calibrated. The Kalman Filter is only worthwhile to "fuse" these two sensors if you can reach a point where the short-term accuracy of the IMU is significantly better than the short-term accuracy of the altimeter.
If you do proceed with the KF, your structure looks generally good. Here are some specific comments:
You model acceleration as -x[2]. (The minus sign is due to your matrix A. I'm not sure why you chose to negate acceleration.) I don't think having acceleration in your state is doing you much good. One of the advantages of the ... + B*u method of using the IMU is that you don't have to retain acceleration (as your B matrix demonstrates). If acceleration is a measurement you have to have it in your state vector because H=[0 0 1].
You have not made any effort to choose P, Q or R. Those are the most important matrices in the KF. Another answer here might help: https://electronics.stackexchange.com/questions/86102/kalman-filter-on-linear-acceleration-for-distance/134975#134975
thanks for your answer!
Until now, I'm using a complementary filter to fuse the acc data with the baro data. All three axis of the acc are already compensated.
Right now, I've got a 1D-kalman filter which reduces noise of the baro output while keeping the phase delay quite smale, thats the reason why I don't use a lowpass filter.
I'm calculating the derivative of the baro data to get velocity based on the baro, which has about 100ms delay.
This velocity is then feed into the first complementary filter together with the acc calculated velocity by integrating it.
The second complementary filter uses this fused velocity (which is drift-free & has nearly no delay) and integrates it to fuse it with the baro altitude data.
This works quite well, but I wanted to try the kalman filter to see, wheter it's possible to get a more accurate data out of it.
In the internet, if found this paper: http://www.actawm.pb.edu.pl/volume/vol8no2/06_2014_004_ROMANIUK_GOSIEWSKI.pdf
It seems to match my "problem" very good, so I decided to use it as a starting point.
The negative sign in my matrix A comes from this, maybe due to their mounting direction. I'm gonna check that ;)
Best Regards
Chris

C++/SDL Gravity in Platformer

I'm currently trying to get a form of gravity (it doesn't need to be EXACTLY gravity, no realism required) into my platformer game, however I'm stumbling over logic on this.
The following code is what I use when the up arrow or W is pressed, (jumping)
if (grounded_)
{
velocity_.y -= JUMP_POWER;
grounded_ = false;
}
In my Player::Update() function I have
velocity_.y += GRAVITY;
There's more in that function but it's irrelevant to the situation.
Currently the two constants are as follows: GRAVITY = 9.8f; and JUMP_POWER = 150.0f;
The main issue I'm having with my gravity that I cannot find the proper balance between my sprite being able to make his jumps, and being way too floaty.
Long story short, my questions is that my sprite's jumps as well as his regular falling from one platform to another are too floaty, any ideas on how to scale it back to something a tad more realistic?
Instead of thinking in terms of the actual values, think in terms of their consequences.
So, the initial velocity is -jump_power, and the acceleration gravity. A little calculus gives
y = -Height = -jump_power * t + 1/2 * gravity * t^2
This assumes a small time step.
Then, the
time_in_flight = 2 * time_to_vertex = jump_power/gravity
and the vertex is
height(time_to_vertex) = jump_power^2/(4 * gravity)
Solving these, and adjusting for time step and fixing negatives
jump_power = (4 * height / time) * timestep_in_secs_per_update
gravity = (2 * jump_power / time) * timestep_in_secs_per_update
That way, you can mess with time and height instead of the less direct parameters. Just use the equations to gravity and jump_power at the start.
const int time = 1.5; //seconds
const int height = 100 //pixels
const int jump_power = (4 * height / time) * timestep_in_secs_per_update;
const int gravity = (2 * jump_power / time) * timestep_in_secs_per_update;
This is a technique from maths, often used to rearrange a family of differential equations in terms of 'dimensionless' variables. This way the variables won't interfere when you try to manipulate the equations characteristics. In this case, you can set the time and keep it constant while changing the power. The sprite will still take the same time to land.
Of course 'real' gravity might not be the best solution. You could set gravity low and just lower the character's height while they are not grounded.
You need think unit system correctly.
The unit of the gravity is meter per second squared. ( m/(s*s) )
The unit of a velocity is meter per second. ( m/s )
The unit of a force is Newton. ( N = kg*m/(s*s) )
Concept example:
float gravity = -9.8; // m/(s*s)
float delta_time = 33.333333e-3f; // s
float mass = 10.0f; // Kg
float force = grounded_ ? 150.0f : gravity; // kg*m/(s*s)
float acceleration = force / mass; // m/(s*s)
float velocity += acceleration * delta_time; // m/s
float position += velocity * delta; // m
It is based on the basic Newton's Equation of motion and Euler's Method.

C++ how to make velocity equal in all directions?

I feel this is a difficult question to articulate, so I have illustrated on this graph (I am using SDL in C++).
Each square represents a pixel on the screen, I want the red pixel to move at the same speed regardless of direction.
If the speed is 8 pixels/sec then after 1 second:
If the user input is right OR down the pixel will arrive at the position marked in blue
If the user input is right AND down it will arrive at the position marked green.
In both cases the pixel has been displaced by 8 pixels, however.. The euclidean distance between red and blue = 8.00 and red and green = 11.31. I want the pixel to arrive at yellow instead.
So I have tried to correct this by declaring a constant speed, then I divide this by the actual displacement, giving me a number I use to multiple the X and Y coordinates and travel back along the trajectory, limiting my speed.
The code looks sorta like this (I have commented the area of interest):
float velX = 0, velY = 0, currentX, currentY;
int time = 0, speed = 300;
//Events
void handleInput(){
if( event.type == SDL_KEYDOWN ){
switch( event.key.keysym.sym ){
case SDLK_UP: {velY -= speed;} break;
case SDLK_DOWN: {velY += speed;} break;
case SDLK_LEFT: {velX -= speed;} break;
case SDLK_RIGHT: {velX += speed;} break;
}
}
else if( event.type == SDL_KEYUP ){
//do the opposite
}
}
//Logic
void move(){
//float dist = sqrt( (velX*velX) + (velY*velY) );
//
//if(dist > 0){
// velX *= speed / dist;
// velY *= speed / dist;
//}
currentX += velX * (get_delta_ticks(&time) / 1000.f);
currentY += velY * (get_delta_ticks(&time) / 1000.f);
set_delta_ticks(&time);
}
//Render
void Player::render(){
apply_surface(currentX, currentY, spriteSheet, screen, &currentClip);
}
So here is my question, I am new to programming games and I'm unsure if this is the CORRECT way to be doing movement.. It seems a bit inefficient in ways, should I be trying to deduce the position based on an angle and the length of the hypotenuse instead? I don't know very much about trigonometry but of course I am keen to learn.
Separate the logical position from the display position.
The logical position will probably need to use floating-point coordinates, and you'll round them to integer pixel coordinates for the display position. You can even do anti-aliasing with this if you want to smooth the movement.
So:
right would have logical unit vector (x,y)=(1.0,0.0)
down would have logical unit vector (x,y)=(0.0,-1.0)
down+right would have logical unit vector (x,y)=(1/sqrt(2),-1/sqrt(2))
every 1/8th of a second, you add the unit vector to your current logical location, and select which pixel to draw. Obviously you can choose different units and update frequencies, but this will give the numbers you asked for.
You need to get the speed in a 2D Space. To get it you have to do a sqrt with both speeds.
curSpeed = sqrt( ( velX * velX ) + (velY * velY ) );
The point is: You counted 8-x and 8-y key press events, which lead to a shortest distance from the origin of v=sqrt(8*8+8*8)=11.31, exactly as you observed.
You should be aware, that, within the time you are measuring, either 8 (only x OR y) or 16 (x plus y) key press events might be sampled, resulting in different "speeds", where speed=number_of_key_events/period_of_time
If you want to travel to the "yellow" spot, there should be only 6 X key press events plus 6 Y key press events in the same period of time in which you sampled the 8 key presses in one of the basic directions.
So there is nothing wrong with your code, and, as the other posters pointed out, your euclidian speed can be calculated using the euclidian distance divided by the sampling period, resulting in v=8 or v=11.31, respectively.
I would start with different user controls: namely absolute speed and direction.
Given speed velAbs and the angle theta, you have
velX = velAbs * cos(theta);
velY = velAbs * sin(theta);
When updating the position, it is typically most convenient to decompose the absolute speed in its X and Y components, update the X and Y positions for the given time interval dt
currentX = velX * dt;
currentY = velY * dt;
whereas for collision impact computations the absolute speed is more relevant.
This will avoid your yellow/green problem because maximum throttle in both the X and Y directions will get you to green. Just let the user set the throttle from 0 to 8 and also set a direction, then you will get to yellow or blue.
Well, it looks like most people forgot about analog input...
Anyway, It should work like this:
velX, velY are floats within [-1.0..1.0] range.
In case of digital input (keyboard, dpad), pressing "left" sets velX to -1, pressing "right" sets velX to 1, etc.
However, if you use analog stick, you put floating point values, where velX == 1.0 corresponds to rightmost position of analog stick, velX == -1.0 corresponds to leftmost position, and so on.
maxSpeed is maximum game movement speed, also float.
With all this in mind, you could calculate next position of object like this:
void move(){
float curVelX = velX, curVelY = velY;
float moveSquared = (curVelX*curVelX + curVelY*curVelY);
if (moveSquared > 1.0f){
float d = sqrtf(moveSquared);
curVelX /= d;
curVelY /= d;
}
currentX += curVelX * maxSpeed * (get_delta_ticks(&time) / 1000.f);
currentY += curVelY * maxSpeed * (get_delta_ticks(&time) / 1000.f);
set_delta_ticks(&time);
}
It seems a bit inefficient in ways,
Look, you have ONE object. When you'll have few hundreds of thousands of them, then you can start worrying about efficiency.
should I be trying to deduce the position based on an angle and the length of the hypotenuse instead?
If your object is torpedo-like and can slowly turn left/right and accelerate/decelerate (you can steer it a bit and make it go faster/slower), then you probably need to store movement direction and linear movement speed.
If your object is some kind of flying orb or rolling ball that can go in any direction it wants, then you should use method similar to the one I described. Have separate velocity for x/y and limit maximum linear velocity using sqrtf.

SFML C++ I'm using delta time wrong... what's the right way

I'm trying to make an asteroids clone and so far I've gotten my ship to fly. However it's speed is also dependent on FPS. So to mitigate that I've read that I'd have to multiply my control variables with deltaTime (time between frames if i gathered right). However when I tried implementing that the ship refused to move. I thought that it's due to possible implicit rounding to 0 (converting to int?) but there are no warnings issued. What am I doing wrong?
Here's how the code looks:
sf::Vector2f newPosition(0,0);
sf::Vector2f velocity(0,0);
float acceleration = 3.0f;
float angle = 0;
float angularVelocity = 5;
float velDecay = 0.99f;
sf::Clock deltaClock;
window.setFramerateLimit(60);
while (window.isOpen())
{
sf::Time deltaTime = deltaClock.restart();
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed || ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
window.close();
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
if(velocity.x < 10)velocity.x += acceleration * deltaTime.asSeconds();
if(velocity.y < 10)velocity.y += acceleration * deltaTime.asSeconds();
angle = player.getRotation() - 90;
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
if(velocity.x > 0)velocity.x -= acceleration * deltaTime.asSeconds();
else velocity.x = 0;
if(velocity.y > 0)velocity.y -= acceleration * deltaTime.asSeconds();
else velocity.y = 0;
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
player.rotate(-angularVelocity);
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
player.rotate(angularVelocity);
}
newPosition.x = player.getPosition().x + (velocity.x * cos(angle * (M_PI / 180.0))) * deltaTime.asSeconds();
newPosition.y = player.getPosition().y + (velocity.y * sin(angle * (M_PI / 180.0))) * deltaTime.asSeconds();
player.setPosition(newPosition);
velocity.x *= velDecay;
velocity.y *= velDecay;
window.clear();
window.draw(background);
window.draw(player);
window.draw(debugText);
window.display();
}
I cannot run your code, thus I cannot be 100% sure, but the following does not look correct:
Calculation of Velocity
velocity.x = (player.getPosition().x + (acceleration * cos(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));
velocity.y = (player.getPosition().y + (acceleration * sin(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));
Try changing it to something like this:
velocity.x = (player.getVelocity().x + (acceleration * cos(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));
velocity.y = (player.getVelocity().y + (acceleration * sin(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));
This is utilizing the simple physics equation vf = vi + a*t but in a x,y component fashion. I believe using Position.x and Position.y would totally throw off that equation.
Note: syntax wise, the code I gave you might not work. Put whatever code into player.getVelocity().x that will get you the current velocity of the player in the X direction. Do the same for the Y direction.
Setting of the new position
I cannot be sure if player.setPosition(velocity); is proper or not. If the setPosition function takes care of doing the following:
newPosition.x = oldPosition.x + (velocity.x/dt)
both in the x and y direction, then that should work.
But if it is simply doing:
newPosition.x = velocity.x
Then I believe this will be wrong and result in improper simulation.
Overall
There could be other mathematical errors in your code. Especially with how you are calculating acceleration. I did not double check this and at the moment I do not have time to. If you make the adjustments I mentioned and its still not working, throw me a comment and I can try looking more when I have the time. I have a game of my own to go work on right now.
Edit1:
Your code looks a lot better. From here, I would add code that changes your acceleration. Say hitting the w key will turn on "thrusters" which give you an acceleration of 2. The acceleration will degrade(go back towards zero)over TIME when no key is being pressed. Not per frame. Before you were multiplying by .99 per frame. Which means acceleration could be zero in half a second if you are getting 120 fps (totally possible in a simple game like this). You need to have it degrade based on your dt variable. Once it hits zero however, you will still have a positive velocity. Usually this velocity is degraded over time due to gravity, but being in space gravity would be very small compared to what you find on earth (-9.8m/s or -32 ft/s). So perhaps you could implement a gravity falloff on your velocity which is also calculated in time
OR
you could ignore gravity and allow them to hit the S key and apply a negative acceleration (-2) and then apply that to your velocity as you have done. This would allow for negative values to degrade your velocity and could be thought of as your ships turning on thrusters in the opposite direction.
Of course you can cheat as the game developer and prevent your velocity from ever going below zero(if you want the player to only move forward) And when you detect a velocity that is negative, set Velocity to 0 and acceleration to 0.
Note: the acceleration "degrading" will happen for both positive and negative, and it will "degrade" towards zero. You will have to tinker with these values and play test to see what feels right. Should acceleration degrade 1 per second? Should you even use the value of 2 and -2 as the acceleration values I mentioned earlier? 2 and -2 might work, but maybe 3 and -3 are better? These are all questions you get to answer yourself through testing it out.
I hope this gives you some more ideas and helps solve your question fully! Let me know how it goes.