How to move relatively to the angle? - c++

I keep looking up on the internet how to move from point A to point B on an angle with a specified distance. When I tried to code it, however, the camera just gets messed up and I'm moving in random directions. I am using SDL/OpenGL with c++ and this is my player function. Right now, I'm trying to get the player to move forwards along the angle.
void player_action()
{
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_MOUSEMOTION:
{
player.rotxl = (screen->w)/2;
player.rotxd = event.motion.x - player.rotxl;
player.rotx = player.rotx + (player.rotxd/4);
if (player.rotx < 0)
{
player.rotx = player.rotx + 360;
};
if (player.rotx > 360)
{
player.rotx = player.rotx - 360;
}
};
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_w:
{player.zvel = 8.0f;}; break;
case SDLK_a:
{player.xvel = 8.0f;}; break;
case SDLK_s:
{player.zvel = -8.0f;}; break;
case SDLK_d:
{player.xvel = -8.0f;}; break;
case SDLK_ESCAPE:
{running = false;}; break;
default: break;
}; break;
case SDL_KEYUP:
switch(event.key.keysym.sym)
{
case SDLK_w:
{player.zvel = 0.0f;}; break;
case SDLK_a:
{player.xvel = 0.0f;}; break;
case SDLK_s:
{player.zvel = 0.0f;}; break;
case SDLK_d:
{player.xvel = 0.0f;}; break;
default: break;
};
break;
};
};
player.x = player.x + float(player.zvel*cos((double)player.rotx));
player.z = player.z + float(player.zvel*sin((double)player.rotx));
glRotatef(player.rotx, 0.0f, 1.0f, 0.0f);
glTranslatef(player.x, player.y, player.z);
SDL_WarpMouse((screen->w/2), (screen->h/2));
};

If I'm correct the math functions sin and cos both take angles in radians, not degrees as player.rotx seems to be. Try the following:
player.x = player.x + float(player.zvel*cos((double)player.rotx*0.0174532925));
player.z = player.z + float(player.zvel*sin((double)player.rotx*0.0174532925));
We're multiplying player.rotx by pi/180, which is how we convert degrees to radians.
Not sure if this is your only problem, but it certainly appears to be one of them.

Two possible sources of the issue I see:
The standard C++ cos/sin function take a function in radians instead of degrees (one radian = 180/pi degrees).
While I'm not familiar with SDL a quick look at the docs makes me think you should be using event.motion.xrel instead of .x. You'll have to change your rotx calculation which should not need the screen width.

Related

How to fix std::shift_right not functioning properly with 2 element std::vector

I am currently working on a Snake game, everything functions as it should except the vector containing the snake body positions. The head of the snake should be the only initial element and after an apple has been eaten a new body part is added along with a trailing tail.
To move the snake body positions so it looks like they are part of the snake I use std::shift_right which works very well except for the first body part added. When the first part is added it is always inside the head/front of the vector. This makes it look like the snake has no growth/parts that follow.
As soon as the second apple is eaten the snake gets a 'tail'. The snake_body vector has now 3 elements while visually only having 2 on the game screen.
One apple has been eaten (snake_body is 2 long) but no tail
Two apples have been eaten (snake_body is 3 long) but has 1 part following instead of 2
How do I fix this?
(Thankyou in advance)
Related sample code:
std::vector <SDL_Point> snake_body = { SDL_Point(_PLAYER_START_POS) };
void Player::Movement()
{
switch (active_valid_input)
{
case SDLK_UP:
snake_body.front() = snake_body.front() + _MOVEMENT_UP;
break;
case SDLK_DOWN:
snake_body.front() = snake_body.front() + _MOVEMENT_DOWN;
break;
case SDLK_RIGHT:
snake_body.front() = snake_body.front() + _MOVEMENT_RIGHT;
break;
case SDLK_LEFT:
snake_body.front() = snake_body.front() + _MOVEMENT_LEFT;
break;
default:
break;
}
std::shift_right(snake_body.begin(), snake_body.end(), 1);
}
void Player::AddSnakePart() noexcept{
snake_body.push_back(snake_body.back());
}
You need to break up your movement, such that the shift happens between calculating the next head position, and assigning that position to head. At the moment your movement function leaves the head part in a moved-from state.
void Player::Movement()
{
SDL_Point next_head;
switch (active_valid_input)
{
case SDLK_UP:
next_head = snake_body.front() + _MOVEMENT_UP;
break;
case SDLK_DOWN:
next_head = snake_body.front() + _MOVEMENT_DOWN;
break;
case SDLK_RIGHT:
next_head = snake_body.front() + _MOVEMENT_RIGHT;
break;
case SDLK_LEFT:
next_head = snake_body.front() + _MOVEMENT_LEFT;
break;
default:
break;
}
std::shift_right(snake_body.begin(), snake_body.end(), 1);
snake_body.front() = next_head;
}
Aside: I would extract that switch into a function:
SDL_Point Player::Direction()
{
switch (active_valid_input)
{
case SDLK_UP: return _MOVEMENT_UP;
case SDLK_DOWN: return _MOVEMENT_DOWN;
case SDLK_RIGHT: return _MOVEMENT_RIGHT;
case SDLK_LEFT: return _MOVEMENT_LEFT;
}
}
void Player::Movement()
{
SDL_Point next_head = snake_body.front() + Direction();
std::shift_right(snake_body.begin(), snake_body.end(), 1);
snake_body.front() = next_head;
}
The issue was a simple matter of when I actually shifted the snake_body vector, I moved the std::shift_right before I applied the change to the front() position, it now functions properly.

Constant in separate namespace using class/struct with a big constuctor

I have a struct:
struct ColorspaceData
{
private:
glm::vec3 sMatrix;
public:
/// X red
float xr;
/// Y red
float yr;
/// Z red
float zr;
/// X green
float xg;
/// Y green
float yg;
/// Z green
float zg;
/// X blue
float xb;
/// y blue
float yb;
/// Z blue
float zb;
/// Whitepoint
WhitepointData *whitepoint;
/// A [3x3] matrix used in linear-gamma transformation function
glm::mat3 linearTransform;
/// Result of linear-gamma transformation function as `LinearTransformData`
TransformData *linearTransformData;
ColorspaceData(float c_xr, float c_yr, float c_xg, float c_yg, float c_xb, float c_yb,
StdIlluminant c_whitepoint, TransformData c_linearTransformData)
{
xr = c_xr / c_yr;
xg = c_xg / c_yg;
xb = c_xb / c_yb;
yr = 1.0f;
yg = 1.0f;
yb = 1.0f;
zr = (1.0f - c_xr - c_yr) / c_yr;
zg = (1.0f - c_xg - c_yg) / c_yg;
zb = (1.0f - c_xb - c_yb) / c_yb;
switch (c_whitepoint)
{
case (StdIlluminant::A):
whitepoint = new WhitepointData(0.44757f, 0.40745f);
break;
case (StdIlluminant::B):
whitepoint = new WhitepointData(0.34842f, 0.35161f);
break;
case (StdIlluminant::C):
whitepoint = new WhitepointData(0.31006f, 0.31616f);
break;
case (StdIlluminant::D50):
whitepoint = new WhitepointData(0.34567f, 0.35850f);
break;
case (StdIlluminant::D55):
whitepoint = new WhitepointData(0.33242f, 0.34743f);
break;
case (StdIlluminant::D65):
whitepoint = new WhitepointData(0.31271f, 0.32902f);
break;
case (StdIlluminant::D75):
whitepoint = new WhitepointData(0.29902f, 0.31485f);
break;
case (StdIlluminant::D93):
whitepoint = new WhitepointData(0.28315f, 0.29711f);
break;
case (StdIlluminant::E):
whitepoint = new WhitepointData(0.33333f, 0.33333f);
break;
case (StdIlluminant::F1):
whitepoint = new WhitepointData(0.31310f, 0.33727f);
break;
case (StdIlluminant::F2):
whitepoint = new WhitepointData(0.37208f, 0.37529f);
break;
case (StdIlluminant::F3):
whitepoint = new WhitepointData(0.40910f, 0.39430f);
break;
case (StdIlluminant::F4):
whitepoint = new WhitepointData(0.44018f, 0.40329f);
break;
case (StdIlluminant::F5):
whitepoint = new WhitepointData(0.31379f, 0.34531f);
break;
case (StdIlluminant::F6):
whitepoint = new WhitepointData(0.37790f, 0.38835f);
break;
case (StdIlluminant::F7):
whitepoint = new WhitepointData(0.31292f, 0.32933f);
break;
case (StdIlluminant::F8):
whitepoint = new WhitepointData(0.34588f, 0.35875f);
break;
case (StdIlluminant::F9):
whitepoint = new WhitepointData(0.37417f, 0.37281f);
break;
case (StdIlluminant::F10):
whitepoint = new WhitepointData(0.34609f, 0.35986f);
break;
case (StdIlluminant::F11):
whitepoint = new WhitepointData(0.38052f, 0.37713f);
break;
case (StdIlluminant::F12):
whitepoint = new WhitepointData(0.43695f, 0.40441f);
break;
case (StdIlluminant::LEDB1):
whitepoint = new WhitepointData(0.4560f, 0.4078f);
break;
case (StdIlluminant::LEDB2):
whitepoint = new WhitepointData(0.4357f, 0.4012f);
break;
case (StdIlluminant::LEDB3):
whitepoint = new WhitepointData(0.3756f, 0.3723f);
break;
case (StdIlluminant::LEDB4):
whitepoint = new WhitepointData(0.3422f, 0.3502f);
break;
case (StdIlluminant::LEDB5):
whitepoint = new WhitepointData(0.3118f, 0.3236f);
break;
case (StdIlluminant::LEDBH1):
whitepoint = new WhitepointData(0.4474f, 0.4066f);
break;
case (StdIlluminant::LEDRGB1):
whitepoint = new WhitepointData(0.4557f, 0.4211f);
break;
case (StdIlluminant::LEDV1):
whitepoint = new WhitepointData(0.4560f, 0.4548f);
break;
case (StdIlluminant::LEDV2):
whitepoint = new WhitepointData(0.3781f, 0.3775f);
break;
case (StdIlluminant::Theatre):
whitepoint = new WhitepointData(0.3141f, 0.351f);
break;
default:
throw new ColorException("Undefined whitepoint value");
}
sMatrix = glm::vec3(whitepoint->x, whitepoint->y, whitepoint->z) * glm::inverse(glm::mat3x3(xr, xg, xb, yr, yg,
yb, zr, zg, zb));
linearTransform = glm::mat3(sMatrix[0] * xr, sMatrix[1] * xg, sMatrix[2] * xb,
sMatrix[0] * yr, sMatrix[1] * yg, sMatrix[2] * yb,
sMatrix[0] * zr, sMatrix[1] * zg, sMatrix[2] * zb);
linearTransformData = &c_linearTransformData;
}
};
I need to declare CT variable. It works with #define:
#define ColorspaceDefaultSRGB ColorspaceData(0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, StdIlluminant::D65, TransformData(1.055f, 0.0031308f, 12.0f / 5.0f, 12.92f, 0.04045f))
But describing constexpr constructor is too many code duplicate like this (I've got an explanation in my previous question):
constexpr ColorspaceData(float p_xr, float p_yr): xr(p_xr), yr(p_yr) ... (other constructor code) {};
Need to describe all lines twice? Declaring a function for constructor? What is the best way to do that?
I suggest a constexpr init method if you can't make the whole constructor constexpr containing all the code that should be duplicated. Note that if a function is constexpr you can call it even if in that case a non constexpr would work too.
constexpr void init(...){
//a lot of stuff
}
ColorspaceData(...){
init(...)
//not constexpr stuff
}
constexpr ColorspaceData(...){
init(...)
//constexpr stuff
}

How do I scale Box2D objects with PPM?

I recently ran into a problem with Box2D where my player was moving at a slow speed. I use LinearImpulse's to move the body but (100, 100) was the fastest it could go.
After researching it a bit, I know that the Box2D body is too large because Box2D uses meters and I'm typing it in pixels. I know I have to scale the bodies using a PPM constant, but it's not quite making sense to me.
I've created a PPM constant and set it to 32.0f.
const float PPM = 32.0f;
Here's how I create the dynamic player body:
void Physics::playerDynamic(float x, float y, float width, float height)
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(x / PPM, y / PPM);
body = world.CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox((64.0f) / PPM, (64.0f) / PPM);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.filter.categoryBits = CATEGORY_PLAYER;
fixtureDef.filter.maskBits = CATEGORY_PLAYER;
fixtureDef.filter.groupIndex = -1;
body->CreateFixture(&fixtureDef);
world.Step(timeStep, velocityIterations, positionIterations);
posX = x;
posY = y;
bodyType = "player";
}
I understand that I need to scale down the body, which I did by dividing it by the PPM. But now the body is really small and won't collide how I'd like it to.
Here is the function where I apply the impulse to the player body:
void Physics::moveBodies()
{
myCollisionDetection myListener;
world.SetContactListener(&myListener);
if (bodyType != "player")
{
}
else if (bodyType == "player")
{
body->SetAwake(true);
float forceX = 0;
float forceY = 0;
switch (moveState)
{
case MS_UP:
forceY = -50;
break;
case MS_DOWN:
forceY = 50;
break;
case MS_LEFT:
forceX = -50;
break;
case MS_RIGHT:
forceX = 50;
break;
case MS_UPRIGHT:
forceY = -50;
forceX = 50;
break;
case MS_UPLEFT:
forceY = -50;
forceX = -50;
break;
case MS_DOWNRIGHT:
forceY = 50;
forceX = 50;
break;
case MS_DOWNLEFT:
forceY = 50;
forceX = -50;
break;
case MS_STOP:
body->SetLinearVelocity(b2Vec2(0, 0));
}
body->ApplyLinearImpulse(b2Vec2(forceX, forceY ), body->GetWorldCenter(), true);
pos = body->GetPosition();
posX = body->GetPosition().x;
posY = body->GetPosition().y;
}
world.Step(timeStep, velocityIterations, positionIterations);
}
I've been researching this for quite a while and it seems simple but I'm having a hard time applying it to my code.
So what do I do from here? Do I scale the body back up?
I'm also using SDL2 for the graphics. Do I need to change anything with the rendering?

SDL stiff movement

i am making a pong game, lPad is left pad and rPad is the right pad, but i have a problem when any pad is moving up and when i release the up button and press down the pad stops for a while and then goes down, the other thing is i can't move both pads when pressing both controls(only one is moving) with this setup:
if(e.type == SDL_KEYDOWN) {
switch(e.key.keysym.sym) {
case SDLK_s:
lPad.y += 8;
if(lPad.y >= s.SCREEN_HEIGHT - lPad.getHeight()) {
lPad.y = s.SCREEN_HEIGHT - lPad.getHeight();
}
break;
case SDLK_w:
lPad.y -= 8;
if(lPad.y <= 0) {
lPad.y = 0;
}
break;
case SDLK_DOWN:
rPad.y += 8;
if(rPad.y >= s.SCREEN_HEIGHT - rPad.getHeight()) {
rPad.y = s.SCREEN_HEIGHT - rPad.getHeight();
}
break;
case SDLK_UP:
rPad.y -= 8;
if(rPad.y <= 0) {
rPad.y = 0;
}
break;
default:
break;
}
}
Any idea how to fix this and make it smooth ?
It's better to use SDL_GetKeyboardState(NULL) as the function to get input. This way, you can get the entire state of the keyboard simultaneously and thus allow for parallel inputs. If you use the while loop, each event will get caught individually, and thus be choppy.
Here is some sample code on how to use it:
const auto * keys = SDL_GetKeyboardState(NULL);
while(!done) {
while(SDL_PollEvent(&e)) {
if(e.type == SDL_QUIT) {
done = true;
}
}
SDL_PumpEvents();
if (keys[SDL_SCANCODE_UP]) {
p1.set_speed(0, -60000 * delta.count());
}
if (keys[SDL_SCANCODE_DOWN]) {
p1.set_speed(0, 60000 * delta.count());
}
if (keys[SDL_SCANCODE_LEFT]) {
p1.set_speed(-60000 * delta.count(), 0);
}
if (keys[SDL_SCANCODE_RIGHT]) {
p1.set_speed(60000 * delta.count(), 0);
}
Also, might I suggest having a speed variable? Using pixels is not a good way to scale movement, as it depends on the resolution of the screen. Using something based on a time step is much more robust.

Object movement in a 2D Array/Grid

I am trying to create a variation of the classic Snake game.
Basically what i am stuck on is how to restrict the snake movement to a 2D array whereby there will be a 20x20 grid.
At the moment, my snake head which is just a shape drawn with a midpoint, moves freely one pixel at a time within the game board. I require the snake to move one grid space at a time.
This is my snake code:
void Snake::move()
{
switch(direction_){
case Direction::North:
position_.y += 1;
break;
case Direction::East:
position_.x += 1;
break;
case Direction::South:
position_.y -= 1;
break;
case Direction::West:
position_.x -= 1;
}
if (position_.x < 6.4) position_.x = 44.8; else if (position_.x > 44.8) position_.x = 6.4;
if (position_.y < 0) position_.y = 38.4; else if (position_.y > 38.4) position_.y = 0;
}
void Snake::render(prg::Canvas& canvas) const
{
canvas.drawCircle(getPosition().x * 20, getPosition().y * 20,19.2,prg::Colour::GREEN);
}
void Snake::changeDirection(Direction new_direction)
{
direction_ = new_direction;
}
This is the code that handles keyboard input/movement
PlayerSnake::PlayerSnake()
{
prg::application.addKeyListener(*this);
}
PlayerSnake::~PlayerSnake()
{
prg::application.removeKeyListener(*this);
}
bool PlayerSnake::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_LEFT_KEY:
changeDirection(Direction::West);
break;
case KeyEvent::KB_RIGHT_KEY:
changeDirection(Direction::East);
break;
case KeyEvent::KB_UP_KEY:
changeDirection(Direction::North);
break;
case KeyEvent::KB_DOWN_KEY:
changeDirection(Direction::South);
break;
}
}
return true;
}
I'm in desperate need of any suggestions and have been racking my brain trying to get the snake to move along a grid. I'm also new-ish to C++ so please understand.
Thanks :)
Don't think of the screen as the data. The screen is a representation of the data. This means that you have to figure out a way to map the data to it's visual equivalent.
If the grid is 20x20, but the screen rendering is 200x200, this implies a 1:10 ratio of pixels to cells. So, one possible method of drawing might look like this. (Sorry, using Processing syntax.)
In processing, one method of drawing a rectangle is using the command rect(int left, int top, int right, int bottom);
So, one implementation might look like this:
void draw_square(int cellx, int celly)
{
rect(cellx*10, celly*10, cellx*10+10, celly*10+10);
}