can't change location of ship, function call not working - c++

This is the source file where the function setLocation() is called (that includes for the graphics header file should have angle brackets, but it disappeared, so I used quotes)
#include "SFML/Graphics.hpp"
#include "ship.h"
const int WINDOW_WIDTH = 500;
const int WINDOW_HEIGHT = 500;
//==============================================================================
int main()
{
sf::RenderWindow window( sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT),
"Delta Quadrant", sf::Style::Titlebar | sf::Style::Close);
window.setFramerateLimit(120);
// this causes loop to execute 120 times a second at most.
// (a delay is automatically added after screen is drawn)
Ship obj;
//ADD Code to set limits on ships location (call setMaxLocation);
//sets position of the ship in the middle of the screen
obj.setLocation(250, 250);
while (window.isOpen())
{
//----------------------------------------------------------
//handle user input (events and keyboard keys being pressed)
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}
//turn left with press of left button
while (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
obj.rotateLeft();
//turn right with press of right button
while (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
obj.rotateRight();
//apply thrust with press of up button
while (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
obj.applyThrust();
//----------------------------------------------------------
//draw new frame
window.clear();
//draw ship
obj.updateLocation();
obj.draw(window);
//redisplay window
window.display();
}
return 0;
}
This is setLocation()'s definition, in the ship source file (same issue with the angle brackets, used quotes again).
#include"cmath"
#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"
#include "vector.h"
#include "ship.h"
//Constants
const double PI = 3.14159;
const double THRUST = 0.005;
const double TURN_SPEED = 1;
//constructor
Ship::Ship(){
maxLocations.x = 500;
maxLocations.y = 500;
radius = 5;
location.x = 0;
location.y = 0;
velocity.x = 0;
velocity.y = 0;
angleDeg = 5;
}
void Ship::setLocation(double x, double y){
//Check and correct for the ship going out of bounds.
if (x < 0)
location.x = 0;
else if (x > maxLocations.x)
location.x -= maxLocations.x;
else
location.x = x;
if (y < 0)
location.y = 0;
else if (y > maxLocations.y)
location.y -= maxLocations.y;
else
location.y = y;
}
void Ship::updateLocation(){
location.x += velocity.x;
location.y -= velocity.y;
//Check and correct for the ship going out of bounds.
if (location.x < 0)
location.x = 0;
else if (location.x > maxLocations.x)
location.x -= maxLocations.x;
if (location.y < 0)
location.y = 0;
else if (location.y > maxLocations.y)
location.y -= maxLocations.y;
}
void Ship::draw(sf::RenderWindow& win) {
//Initializes the Ship class to an object
Ship obj;
// draw ship
sf::ConvexShape ship;
ship.setPointCount(3);
ship.setPoint(0, sf::Vector2f(10, 0));
ship.setPoint(1, sf::Vector2f(0, 25));
ship.setPoint(2, sf::Vector2f(20, 25));
sf::Vector2f midpoint(10,15);
ship.setOrigin(midpoint);
ship.setFillColor(sf::Color(0, 0, 0));
ship.setOutlineThickness(1);
ship.setOutlineColor(sf::Color(255, 255, 255));
ship.setPosition(obj.getLocation().x, obj.getLocation().y);
obj.setAngle(obj.getAngle());
win.draw(ship);
}
}
Finally this is the header file where the setLocation() prototype is located
#ifndef SHIP_H
#define SHIP_H
#include "vector.h"
class Ship {
private:
Vector maxLocations; //maximum allowable values for location
Vector location; //current location (x,y)
Vector velocity; //current velocity (in pixels/frame)
double angleDeg; //angle ship is facing, in degrees
double radius; //gross radius of ship (for collision detection)
public:
//constructor
Ship();
//=============================================
//mutators
void setLocation(double x, double y);
void updateLocation();
void draw(sf::RenderWindow& win);
...
};
#endif
My issue is when I call setLocation() it doesn't change the location vector (as defined in the second source file) from (0, 0). When compiled the ship remains at (0, 0). What I want to happen is when I call setLocation(), the ship needs to change from (0, 0) to (250, 250), thus the ship on compiling starts in the middle of the screen, not a corner.

The problem at hand is you are not using the Ship object that you believe you are using. What I mean by that is you aren't drawing the ship object in your main function, instead you are creating a temporary ship object in your Ship::draw() method and then using that ships position to draw your shape. So the position will always be (0, 0) because your temp ship object get's initialized every frame with the position (0, 0) and then you use that temporary ship's position to draw your shape.
To make this more clear let's look at your code.
void Ship::draw(sf::RenderWindow& win) {
// This is a temporary object and is not the same as the one you defined in your
// main.cpp file. When this object is constructed it's location is (0, 0) by
// default.
Ship obj;
// draw ship
sf::ConvexShape ship;
ship.setPointCount(3);
ship.setPoint(0, sf::Vector2f(10, 0));
ship.setPoint(1, sf::Vector2f(0, 25));
ship.setPoint(2, sf::Vector2f(20, 25));
sf::Vector2f midpoint(10,15);
ship.setOrigin(midpoint);
ship.setFillColor(sf::Color(0, 0, 0));
ship.setOutlineThickness(1);
ship.setOutlineColor(sf::Color(255, 255, 255));
// Here we are setting the shapes position to the position of the temporary
// Ship object that you defined above. Remember that it's default location is
// (0, 0) when constructed and you never changed it's location in the draw
// method.
ship.setPosition(obj.getLocation().x, obj.getLocation().y);
obj.setAngle(obj.getAngle());
win.draw(ship);
}
Now how do you fix this? That is easy. All you need to do is get rid of that temporary ship object in your draw method and instead use your location vector to set the position of the shape. Like this.
ship.setPosition(location.x, location.y);
It might be beneficial for your to study up on how objects and classes work in C++ before continuing on. It seems you might not understand exactly how they work and how different scopes of code work. But I will leave that for your to decide what to do.
Though I would like to point out a few other pointers for you.
1 - In your draw method you are updating the position of objects. This is generally a bad thing to do and you should avoid it. All updating code like when you change a objects position should happen in a update function/method. The drawing methods of entities should only have drawing code in them. The reason for this is because usually drawing and updating happen at different time steps in the main game loop.
2 - Your updateLocation method won't work like you think it will. The reason is because you are doing location.y -= velocity.y; when in reality you should be adding the velocity of y to location just like you did with x.
3 - I would advise against using sfml's setFrameRateLimit() method and instead use your own timestep which will be much more accurate (SFML's version uses sf::Sleep which is notorious for being inaccurate) and give you much more control over your time step. If you don't know about time steps I would highly suggest reading this great article on it. It will save you hours of headaches. http://gafferongames.com/game-physics/fix-your-timestep/
4 - Let's take a look at this line.
else if (x > maxLocations.x)
location.x -= maxLocations.x;
Now let's say that maxLocations.x = 500 like in your example and let's also say that location.x = 550. Do you really want the location to be x = 50? Wouldn't a more logical step be to set x to x = maxLocations.x? Not sure if this is what you wanted but thought I would point it out.
Hopefully this will help you a bit and I would always be glad to answer any other questions you might have. Wish you the best of luck with your project.

Related

SFML multiple objects with random values and their collisions

I'm pretty new with SMFL and C++ for the moment and I've got stuck while I was creating very simple physics simulator with balls.
Its my main.cpp:
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Event.hpp>
#include "ball.hpp"
int main()
{
/*create window settings*/
sf::ContextSettings settings;
/*create window*/
sf::RenderWindow window;
window.create(sf::VideoMode(600, 600), "Simple Physics", sf::Style::Default, settings);
/*create ball(s)*/
Ball ball;
/*Main loop*/
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color::Black);
// call ball.update(); and ball.draw();
balls.update();
balls.draw(window);
window.display();
}
}
And ball.hpp :
#include <SFML/Graphics/CircleShape.hpp>
class Ball
{
public:
int minXY = 0;
int maxXY = 600;
int ballRadius = rand() % 50 + 5;
int random = rand() % maxXY + 1; //random XY position
// vector for positions
//sf::Vector2f pos{random, random}; -> I Guess this should be included into loop
sf::Vector2f pos{100, 300};
// vector for velocity
sf::Vector2f vel{ 0.1, 0.1 };
void update()
{
// factors influence velocity
// update position based on velocity
pos.x += vel.x;
pos.y += vel.y;
if (pos.x + ballRadius*2 > maxXY || pos.x < minXY) vel.x = -vel.x; //boundary cond
if (pos.y + ballRadius*2 > maxXY || pos.y < minXY) vel.y = -vel.y; //boundary cond
}
void draw(sf::RenderWindow& window)
{
// draw ball to the window using position vector
sf::CircleShape circle(ballRadius);
circle.setPosition(pos.x, pos.y);
circle.setFillColor(sf::Color::White);
window.draw(circle);
}
};
Now, I want to draw multiple cirlcles, with random size, velocity and colors.
They should bounce on collision, i've got some ideas for that but can't really try them without multiple balls.
I've tried to make it with simple for loop, but it's not working.
After reading and watching many tutorials I'm still in this point so it would be great if somebody can help me up with ANY working example or some improvements to my code.
I will not give you working example but I think I can point you in the right direction:
You need some kind of Constructor for your Ball class, and given the example you should probably learn more about classes in general (for example here).
Then you should store balls you spawn from main loop in some kind of container. If you do know the number of balls you want to spawn std::array should be you choice, otherwise std::vector is the way to go.
You need some function to check collisions, static member function of Ball class should do the trick.

my if condition is not working on processing language

I'm working on Processing language for my assignment. It's an animation. The animation object(a ball) needs to go from top to bottom. I have declared variable as float x,y. Whenever I put if condition to increase size of it by 1 but it's not moving a single inch.
float x;
float y;
size(600, 400)
x = 0.4*width/8;
y = 0.4*height/8;
ellipse( width/2, x, 0.8*width/8, 0.8*width/8);
ellipse( y, height/2, 0.8*height/8, 0.8*height/8);
if(x < height){
x = x+1;
}
if(y < width){
y=y+1;
}
I am expecting the output as- the ball located on top moves towards down and stops at the bottom and the left ball moves to right and stops at the rightmost point.
You're using Processing in "static mode" which means your code runs once and then is done. Nothing happens after you reach the end of your code.
To take advantage of Processing's 60 FPS rendering loop, you need to specify setup() and draw() functions. Something like this:
float circleY;
void setup(){
size(200, 200);
circleY = height/2;
}
void draw(){
background(200);
ellipse(100, circleY, 20, 20);
circleY = circleY + 1;
}
Shameless self-promotion: here is a tutorial on animation in Processing.

Why is my collision response not stopping my player from going through walls (SDL2, C++)?

While I found many problems that are similar too mine, none of the solutions solved my problem.
I've been experimenting with SDL2 in C++ (Visual C++) and the entity-component-system (ECS). But I just can't figure out the bug in my collision response.
So here it is: My player sometimes gets set back to its origin when it encounters something like a rock (a simple gray tile). But sometimes it goes right through and gets stuck or ends up on the other side.
I can only assume it has something to do with the data changed in between frames, so it isn't always caught. But for the life of me I can't figure it out.
Here is my rectangular detection method:
bool Collision::RectIntersect(const SDL_Rect& a, const SDL_Rect& b, SDL_Rect& intersect)
{
intersect = { 0, 0, 0, 0 };
int leftX = std::max(a.x, b.x);
int rightX = std::min(a.x + a.w, b.x + b.w);
int topY = std::max(a.y, b.y);
int bottomY = std::min(a.y + a.h, b.y + b.h);
if (leftX < rightX && topY < bottomY)
{
intersect = { leftX, topY, rightX - leftX, bottomY - topY };
return true;
}
return false;
}
Here is my snippet where my inputs are handled and subsequently any collision detections are resolved before the code actually moves anything:
void InputComponent::handleEvents(SDL_Event* e)
{
const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
if (e != nullptr)
{
/*
keyHeld: array of 4 for each direction (+/- x, +/- y (WASD))
hold value true, if pressed down, otherwise false
*/
if (keyboardState[SDL_SCANCODE_A])
{
keyHeld[0] = true;
}
else
{
keyHeld[0] = false;
}
if (keyboardState[SDL_SCANCODE_D])
{
keyHeld[1] = true;
}
else
{
keyHeld[1] = false;
}
if (keyboardState[SDL_SCANCODE_W])
{
keyHeld[2] = true;
}
else
{
keyHeld[2] = false;
}
if (keyboardState[SDL_SCANCODE_S])
{
keyHeld[3] = true;
}
else
{
keyHeld[3] = false;
}
}
/*
tmpVel: Vector to store the assumed velocity in x- and y-direction
*/
Vector2D tmpVel(0.0f, 0.0f);
// left and right (A and D)
if (keyHeld[0] && !keyHeld[1]) // left
{
tmpVel.x = -1.0f;
}
else if (!keyHeld[0] && keyHeld[1]) // right
{
tmpVel.x = 1.0f;
}
else
{
tmpVel.x = 0.0f; // left and right cancel each other out
}
// up and down (W and S)
if (keyHeld[2] && !keyHeld[3]) // up
{
tmpVel.y = -1.0f;
}
else if (!keyHeld[2] && keyHeld[3]) // down
{
tmpVel.y = 1.0f;
}
else
{
tmpVel.y = 0.0f; // up and down cancel each other out
}
/*
check for collision with presumed direction according to tmpVel
*/
SDL_Rect intersection;
// get current player position
SDL_Rect movedPlayer = entity->getComponent<CollisionComponent>().getCollider();
// add trajectory of theoretical movement
movedPlayer.x += static_cast<int>(tmpVel.x * vel_->getSpeed());
movedPlayer.y += static_cast<int>(tmpVel.y * vel_->getSpeed());
bool hasCollided = false;
// collect all collidable objects
for (auto& c : manager_->getGroup(GroupLabel::GR_COLLIDERS))
{
// check player against each collidable tile
//if (SDL_IntersectRect(&movedPlayer, &c->getComponent<CollisionComponent>().getCollider(), &intersection))
if (Collision::RectIntersect(movedPlayer, c->getComponent<CollisionComponent>().getCollider(), intersection))
{
// collision on x-axis
if (intersection.w > 0)
{
// set velocity on x-axis to 0
vel_->setVelocityX(0.0f);
// reset player position back according to width of intersected rectangle
pos_->setPosX(pos_->getPos().x + (static_cast<float>(intersection.w) * (-tmpVel.x)));
}
// collision on y-axis
if (intersection.h > 0)
{
// set velocity on y-axis to 0
vel_->setVelocityY(0.0f);
// reset player position back according to height of intersected rectangle
pos_->setPosY(pos_->getPos().y + (static_cast<float>(intersection.h) * (-tmpVel.y)));
}
hasCollided = true;
}
}
if (!hasCollided)
{
vel_->setVelocity(tmpVel);
}
}
Can anybody put me in the right direction?
What happens when the right edge of the player exactly equals the left edge of the rock? It looks like the collision is not detected, since the test is for (leftX < rightX). So the velocity is updated and the player is moved by the velocity. (It's odd that you simply update the velocity and later move the player instead of just moving them to the new already calculated position.) If you change the check to (leftX <= rightX), does the problem persist?
As far as I can see there are two things wrong with your collision detection. The first is that you're testing (leftX < rightX && topY < bottomY) when you should be testing (leftX <= rightX && topY <= bottomY). If you fix this your code will work in most situations.
The second problem you've got, which may not become apparent straight away, is that your are performing collision detection for discreet points in time. If your player has a large enough velocity vector you may end up with this situation:
Update 1: Player is in front of wall travelling towards it. AABB test shows no collision.
Update 2: Player is behind wall travelling away from it. AABB test shows no collision.
Your AABB test is correct and yet the player has passed through the wall. The naive approach to fixing this is to test more often (update 1.5 may have shown a collision), or to limit player velocity. Both approaches will require a lot of fine tuning especially if you're dealing with objects that can move at different speeds and walls with differing thickness.
A more robust approach is to take account of velocity in your test. Since you know the velocity of your AABB you can project this shape along its velocity vector. If you do this for both AABBs you'll end up with two elongated shapes which you can test against each other. If they overlap then you know that their paths cross and that there may be a collision.
Of course, knowing that there might be a collision is not hugely helpful. The problem is one AABB may be moving very slowly and the other very quickly so even though they both pass through the same space (their elongated shapes intersect) they don't pass through it at the same time.
Figuring out whether they both pass through the same space at the same time is hard, so instead we cheat. If you subtract the velocity of B from the velocity of A and then use this modified velocity to project the elongated shape of A, you can effectively treat B as a stationary object and still get the correct result. Knowing this, your test is now "does B overlap the elongated shape of A?". This is just a simple AABB vs Ngon problem.
While the above will give you a boolean as to whether two moving AABBs collide it will not tell you when they collide which is also useful for calculating things like rebounds.
I would very much recommend the book Real Time Collision Detection by Christer Ericson which is pretty much the go to book on collision detection for any aspiring game developer.
The following is a code snippet from the CD-ROM which accompanies the book. It tests a moving AABB against another moving AABB and also provides a time of first contact.
// Intersect AABBs ‘a’ and ‘b’ moving with constant velocities va and vb.
// On intersection, return time of first and last contact in tfirst and tlast
int IntersectMovingAABBAABB(AABB a, AABB b, Vector va, Vector vb, float &tfirst, float &tlast)
{
// Exit early if ‘a’ and ‘b’ initially overlapping
if (TestAABBAABB(a, b)) {
tfirst = tlast = 0.0f;
return 1;
}
// Use relative velocity; effectively treating ’a’ as stationary
Vector v = vb - va;
// Initialize times of first and last contact
tfirst = 0.0f;
tlast = 1.0f;
// For each axis, determine times of first and last contact, if any
for (int i = 0; i < 3; i++) {
if (v[i] < 0.0f) {
if (b.max[i] < a.min[i]) return 0;
// Nonintersecting and moving apart
if (a.max[i] < b.min[i]) tfirst = Max((a.max[i] - b.min[i]) / v[i], tfirst);
if (b.max[i] > a.min[i]) tlast = Min((a.min[i] - b.max[i]) / v[i], tlast);
}
if (v[i] > 0.0f) {
if (b.min[i] > a.max[i]) return 0;
// Nonintersecting and moving apart
if (b.max[i] < a.min[i]) tfirst = Max((a.min[i] - b.max[i]) / v[i], tfirst);
if (a.max[i] > b.min[i]) tlast = Min((a.max[i] - b.min[i]) / v[i], tlast);
}
// No overlap possible if time of first contact occurs after time of last contact
if (tfirst > tlast) return 0;
}
return 1;
}
The following attribution is required by Elsevier's Software License Agreement:
“from Real-Time Collision Detection by Christer Ericson, published by Morgan Kaufmann Publishers, © 2005 Elsevier Inc”

How can I set gravity using this code? SFML/C++ [duplicate]

This question already has an answer here:
How can I set gravity using this code?
(1 answer)
Closed 8 years ago.
I am trying to make a game and am stuck on gravity..... In the following code a rectangle stands for a player and when I press up key it moves in y-axis but when I activate gravity on it (i.e resetting it's previous position) it does not animate (i.e. it does not jumps) instead it just stays in it's position.I know why it it happening. Because it is just staying in its position because when I press Up key it executes the code rectangle.setPosition(0, 350). Yeah I want it to do that but I also want to see my player in movement. I am using SFML library of C++. Please Help!
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Gravity");
sf::RectangleShape rectangle;
rectangle.setSize(sf::Vector2f(100, 100));
rectangle.setFillColor(sf::Color::Black);
rectangle.setPosition(sf::Vector2f(10, 350));
while(window.isOpen())
{
sf::Event Event;
while(window.pollEvent(Event))
{
if(Event.type == sf::Event::Closed)
{
window.close();
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
rectangle.move(0, -1);
}
if(rectangle.getPosition().y >= 350-1)
{
rectangle.setPosition(0, 350);
}
window.display();
window.clear(sf::Color::Cyan);
window.draw(rectangle);
}
}
Gravity is an acceleration: that is, the double derivative of displacement. So you can't directly set the displacement (as you're currently doing) to get a nice representation of gravity.
An approach would be to create an entity class of your own, adding members for velocity, acceleration; alongside sf::RectangleShape's internal displacement; then have member functions operate on initialization/every frame - a quick & dirty example (untested):
class SomeEntity {
public:
SomeEntity( float x_pos, float y_pos ) : position(x_pos, y_pos) {
m_shape.setSize(sf::Vector2f(100, 100));
m_shape.setFillColor(sf::Color::Black);
m_shape.setPosition(sf::Vector2f(x, y));
// Constants at the moment (initial velocity up, then gravity pulls down)
velocity = sf::Vector2f(0, -30);
acceleration = sf::Vector2f(0, 9.81); //Earth's acceleration
}
void Step() { // TODO: use delta time
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
velocity.y -= 1;
}
velocity.x += acceleration.x;
velocity.y += acceleration.y;
x += velocity.x;
y += velocity.y;
m_shape.setPosition(x, y);
}
void Draw(sf::RenderWindow &window) {
window.draw(m_shape);
}
private:
sf::RectangleShape m_shape;
sf::Vector2f position, velocity, acceleration;
}
Which also means you can rewrite your application so it's a little cleaner:
SomeEntity ent(360, 0);
while(window.isOpen()) {
sf::Event Event;
while(window.pollEvent(Event)) {
if(Event.type == sf::Event::Closed) {
window.close();
}
}
ent.Step();
window.display();
window.clear(sf::Color::Cyan);
ent.Draw();
}
Seeing as you're setting your rectangle to x = 0, y = 350 repeatedly, I'll work under the assumption that that's your 'ground plane'. To achieve that, you just want to check whether the entity is under the ground plane, and reset it's position to the ground plane if it is - either in the entity's 'Step' function or directly in your main loop. In the long run, you might be better off using an entity manager/third party physics engine to do this sort of thing (a la box2D, for example)
You can do something like this instead :
if(rectangle.getPosition().y > 350)
{
rectangle.move(0, 0.01);
}

OpenGL Translation - Coordinate

I'm attempting to make a simple game that allows you to jump around the screen and onto the platforms without leaving the screen or falling through the platforms.
So far i have managed to create a little 2D square which i refer to as my sprite or character along with 2 platforms.
I have finally made my sprite move up, down, left and right (no jump yet) but my collision for leaving the screen isn't working how i thought it would...
What i'm doing is using glTranslate(x,y,z) to move my sprite depending on the key input from the keyboard and testing a collision between the sprite and the screen before. Unfortunately i think im getting confused with the coordinates being used for the translation inside my collisionScreen.
Something to note is, i can not take my sprite past the origin of where the sprite was initially drawn ie "0" no matter where or when the sprite is drawn.
It seems that:
void collisionScreen (int x, int y)
{
//stops sprite leaving current screen size
if(x_pos < 0 || x_pos+s1_W > winX)
x_pos -= x_pos;
if(y_pos < 0 || y_pos+s1_H > winY)
y_pos -= y_pos;
}
doesn't actually use the x and y parameters you pass into it.
Perhaps you meant something like this:
void collisionScreen (int x, int y)
{
//stops sprite leaving current screen size
if(x_pos < 0)
x_pos = 0;
else if(x_pos+s1_W > winX)
x_pos = winX - s1_W;
if(y_pos < 0)
y_pos = 0;
else if(y_pos+s1_H > winY)
y_pos = winY - s1_H;
}
Also, your sprite will not pass 0 as that is what this method is designed to do.