Pure virtual function, inheritance and call by reference from inherited vitrual function

Hello all. I am implementing a move() method in a Car class. This Car class is inherited from a Vehicle class and the Vehicle class has a pure virtual move() function:

 
  virtual void move() = 0;


Now, I want to implement a move() method in inherited Car class. However, I should use some parameters like x, y coordinates and angle of the car. Therefore, I should use reference to that parameters when I write the function, but I cannot use these parameters.

This is my move() function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    void move() {
        if(x < 218 && y > 120 && y < 1000 && fabs(angle - 270.0f) < 0.4) {
            y -= 1;
            x += 0;
            while(angle < 360.0f)
                angle += 1;
        }

        if(x > 216 && x < 1000 && y < 125 && fabs(angle - 360.0f) < 0.4) {
            x += 1;
            y += 0;
        }

        this->sprite.setPosition(sf::Vector2f(x, y));
        this->sprite.setRotation(angle);
    }


If I call this from main() function, the car does not move, it only rotates. How should I implement this to change the coordinate of the car?
I think you're going to have to explain some more, because your move code doesn't make much sense to me.

First, fabs(angle - 270.0f) < 0.4
This is only be true if angle is in range (269.6f, 270.4f).
Let's say that angle = 269.7f, and x and y meet the other conditions. After the while loop on line 5, angle will be 360.7f. Now, fabs(360.7f - 360.0f) will be 0.7, which is not less than 0.4.
If the initial conditions are
1
2
3
    car.x = 217;
    car.y = 122;
    car.angle = 270.f;

then you will get the x coordinate to increase through multiple calls, but still I'm not sure if that's intended?

Your angle will only ever move once, and your y position will only ever move by -1, but your x can get larger over multiple calls. So please explain more what you're trying to accomplish.
Last edited on
Is sprite.setPosition() supposed to cause the car to move?

EDIT: Also, where are x and y in your code snippet defined?
Last edited on
I assume x and y are either in the Car class or the Vehicle class.
e.g. something like this might work as a minimal example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Example program
#include <iostream>
#include <cmath>

using std::fabs;

class Car
{
  public:
    int x;
    int y;
    float angle = 0;

    void move() {
        if(x < 218 && y > 120 && y < 1000 && fabs(angle - 270.0f) < 0.4) {
            y -= 1;
            x += 0;
            while(angle < 360.0f)
                angle += 1;
        }

        if(x > 216 && x < 1000 && y < 125 && fabs(angle - 360.0f) < 0.4) {
            x += 1;
            y += 0;
        }

        //this->sprite.setPosition(sf::Vector2f(x, y));
        //this->sprite.setRotation(angle);
    }
};

std::ostream& operator<<(std::ostream& os, const Car& car)
{
    return os << car.x << " " << car.y << " " << car.angle << '\n';
}

int main()
{
    Car car;
    car.x = 217;
    car.y = 122;
    car.angle = 270.f;
    
    std::cout << car << '\n';
    car.move();
    std::cout << car << '\n';
    car.move();
    std::cout << car << '\n';
    car.move();
    std::cout << car << '\n';
}


217 122 270

218 121 360

219 121 360

220 121 360
Last edited on
Utko, if x==0 and y==0 upon entry, the car won't move. There are many other values that won't trigger either of the first two conditions.

1
2
3
            while(angle < 360.0f)
                angle += 1;
        }
This sets angle to some value between 360 and 361. Is that your intention?
Hello again. Sorry for late response.

My aim is move the car from some point to another and if car is at the end of the road, it rotates. This is the initial states of the vehicles:

https://imgur.com/wtBA4TT

Initially, the rotation of the first bus is 270 degree, it rotates at the end of the road, but does not move. Then its rotation becomes 360.

My aim for using fabs is that sometimes, angles may not be exact values such as 270.0f, 90.0f, etc.

I increase & decrease x and y coordinates according to the place of car.
If there is a Car that isn't moving how you expect, tell us what the initial conditions for x, y, and angle are, and we can tell you how the car will advance after each call to move(), not accounting for other external changes.

But it's hard for us to say more. What dhayden/I said still stands; the logic inside move seems very strict, in most cases the if statements won't be entered.
The initial conditions come from waypoint's position.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "roadtile.h"

#ifndef WAYPOINT_H
#define WAYPOINT_H

typedef enum {
    DOWN = 1,
    LEFT = 2,
    UP = 3,
    RIGHT = 4
} tWaypointDir;

class Waypoint
{
    float x, y;
    int dir;
    int next1, next2, next3;
    sf::Texture texture;
    sf::Sprite sprite;

public:
    //Display waypoints
    Waypoint(tWaypointDir dir, tRoadTileType type, int row, int col, int idx, int next1, int next2, int next3);
    
    int getNext(); //Get next waypoint randomly
    void getPosition(float &x, float &y, float &dir) const { x = this->x; y = this->y; dir = this->dir; }
    void setPosition(float x, float y, float dir) { this->x = x; this->y = y; this->dir = dir; }
    void draw(sf::RenderWindow *window) {window->draw(sprite);}
};

#endif // WAYPOINT_H 


In main() function, I created the waypoints:

1
2
3
4
5
6
7
Waypoint waypoints[] = {
        {UP, CTL, 0, 0, 0, 1, -1, -1}, {RIGHT, CTL, 0, 0, 1, 0, -1, -1},
        {RIGHT, HOR, 0, 1, 0, 3, -1, -1}, {RIGHT, HOR, 0, 1, 1, 2, -1, -1},
        {RIGHT, TTOP, 0, 2, 0, 5, 6, -1}, {DOWN, TTOP, 0, 2, 1, 4, 6, -1},
        {RIGHT, TTOP, 0, 2, 2, 4, 5, -1}, {RIGHT, HOR, 0, 3, 0, 8, -1, -1},
        ...
}


Then, I got the position of first waypoint using:

1
2
float x, y, dir;
waypoints[0].getPosition(x, y, dir);


If I print the initial x, y, and dir:

x 118 y 218 dir 3

where dir = angle / 90.

The car should follow the waypoints when it moves.
My aim for using fabs is that sometimes, angles may not be exact values such as 270.0f, 90.0f, etc.
It appears to me that you don't need angles at all. Just use tWayPointDir to represent the direction.

Same with positions X and Y. Do they really need to be floats?Don't they just represent tile positions on the board?

In class Waypoint, what do next1, next2, and next3 represent?

In the constructor, what does idx represent?

When you hit a wall, what direction do you rotate? I assume it's always clockwise 90 degrees.

It sounds to me like move should be something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void move()
{
    // Values to add to x and y, indexed by tWaypointDir. This assumes
    // that positive Y goes up.
    //                DOWN  LEFT  UP  RIGHT
    static int dx[4] { 0,    -1,   0,   1};
    static int dy[4] {-1,     0,   1,   0};

    // Attempt to move
    x += dx[dir];
    y += dy[dir];

    // If you hit a wall, then restore the position and rotate
    // clockwise.  I've used MaxX and MaxY to represent the far edges
    // in the X/Y directions
    if (x < 0 || x >= MaxX || y < 0 || y >= MaxY) {
        x -= dx[dir];
        y -= dx[dir];
        dir = (dir+1) % 4;
    }

    this->sprite.setPosition(sf::Vector2f(x, y));
    this->sprite.setRotation(dir*90); // Is this right?
}



Hello again.

Thank you for all your comments and contributions.

next1, next2 and next3 are possible next waypoint index. For example, let's look at top left part of the road. First waypoint is UP and second one is RIGHT. Let's say the index of them are 0 and 1 respectively. If the car is at the UP waypoint, then next1 becomes 1 (index of RIGHT), and next2, next3 are -1 since there is no other waypoint on this road tile.

In constructor, idx means internal index of the waypoint in each road tile. For example, if we look at CROSS road tile, there are 4 waypoints and hence, there are 4 idx number. These are 0, 1, 2, and 3. idx 0 is RIGHT one.

This image states the internal indexes:

https://imgur.com/CnjaC3f

When you hit a wall, what direction do you rotate? I assume it's always clockwise 90 degrees.

Actually, the rotation is sometimes counter clockwise especially in the CROSS road tile. Also, I want the car rotate smoothly, not just 90 degree thus I used:

angle += 1

this->sprite.setRotation(dir*90); // Is this right?


Yes, this is correct.
The problem is that your "angle += 1" is inside a while loop that just repeats until angle > 360, so it won't be smooth. It goes immediately from 270 to 360 within one function call.

You need to have some sort of state that you keep track of that determines if the car is currently in the process of turning or not, and smoothly increment the car every frame until you're going straight again. I've written code like this before, but I don't have it on hand.. I could try to write a simplified example.

Edit: Something like this,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <cmath>

struct Vector2f {
    float x;
    float y;
};

std::ostream& operator<<(std::ostream& os, const Vector2f& vec)
{
    return os << vec.x << ", " << vec.y << "; ";
}

enum class TurningState {
    Straight, Left, Right  
};

const float Pi = 3.14159265359;

class Car {
  public:
    Vector2f position;
    float angle; // radians;
    TurningState turning;
    float delta_angle = Pi / 100.0;

    void startTurningLeft()
    {
        turning = TurningState::Left;
    }
    void startTurningRight()
    {
        turning = TurningState::Right;
    }
    void goStraight()
    {
        turning = TurningState::Straight;
    }
    
    void update()
    {
        if (turning == TurningState::Left)
        {
            // rotate velocity vector to the left (counter-clockwise)
            angle += delta_angle;
            if (angle > 2.0 * Pi)
            {
                angle -= 2.0 * Pi;
            }
        }
        else if (turning == TurningState::Right)
        {
            // rotate velocity vector to the right (clockwise)
            angle -= delta_angle;
            if (angle < 0.0)
            {
                angle += 2.0 * Pi;
            }
        }
        // else: No change in velocity. Keep going forward.
        
        Vector2f velocity { std::cos(angle), std::sin(angle) };
        position.x += velocity.x;
        position.y += velocity.y;
    }
};

int main()
{
    using std::cout;
    
    // Initially going in the +x direction (angle = 0.0), not turning
    Car car { {0.0, 0.0}, 0.0, TurningState::Straight };
    
    // continue going straight for 50 frames
    for (int i = 0; i < 50; i++)
    {
        car.update();
        cout << car.position << '\n';
    }
    
    // start turning left for 100 frames
    car.startTurningLeft();
    for (int i = 0; i < 100; i++)
    {
        car.update();
        cout << car.position << '\n';
    }
    
    // start turning right for 100 frames
    car.startTurningRight();
    for (int i = 0; i < 100; i++)
    {
        car.update();
        cout << car.position << '\n';
    }
}


In my example, the the car is going straight in the +x direction, then turns 180-degrees to the left over 100 frames, then turns 180-degrees to the right over 100 frames (e.g. making a big S).
Last edited on
Can you define what a waypoint is? I think you're saying it's one of the crosswalks.

let's look at top left part of the road. First waypoint is UP and second one is RIGHT.
I'm confused. If you're at the top left part of the road, you can't go up, so how can there be a waypoint there?

the rotation is sometimes counter clockwise especially in the CROSS road tile.
What determines the direction of rotation when you come to the cross in the middle (or one of the T intersections)?

Can you post the full text of the assignment? There seems to be a lot more going on here than what you've stated.
Ok, I added the full assignment. This is webpage link for the assignment:

https://gofile.io/d/ZQLcAd

Sorry for wasting your time.
Thanks for the details.

As often happens, the assignment has some inconsistencies and other problems. The biggest one I see is the waypoint constructor. It requires that you know the index of the next 3 waypoints. Each of those waypoints needs to know the global index of their next waypoints, etc. So it would seem that you need to know the global index of all the waypoints before you can construct any of them. Ask your prof if you can add setNext1(), setNext2() and setNext3() members to class Waypoint.

Since the parameters to Vehicle::move() are the new location & angle, they should be const references, or values. You may want to verify that move() doesn't change the parameters.

Why do you have a separate Car class with it's own move() method? Why not just use the vehicle class?

Your Car::move() method doesn't meet the requirements of move() as described in the assignment. I suggest two methods: nextPos() that determines where the car will move to, and move() that actually moves it to the new location.

I don't see any reason to deal angles other than up, down, left and right. When a car turns, I think it can just appear in the new direction.

At a high level, I think the logic for each car is:
1
2
nextPos();
move();


Hmm. So each car knows the waypoint that it's heading towards.

nextPos() is something like this:
1
2
3
4
5
6
7
void Vehicle::nextPos(int &x, int &y, tWaypointDir &dir)
{
    if (you have reached the next waypoint) {
        nextWayPoint = nextWayPoint.getNext();
    }
    update x, y, angle to move closer to nextWayPoint.
}
Hello.

Ask your prof if you can add setNext1(), setNext2() and setNext3() members to class Waypoint.

We are free to create new functions and variables. We can add these members.

Why do you have a separate Car class with it's own move() method? Why not just use the vehicle class?

We also implement a Bus class which has additionally bus stops vector variable that hold the index of the stops. Therefore, our instructor want two different inherited class.

Your Car::move() method doesn't meet the requirements of move() as described in the assignment. I suggest two methods: nextPos() that determines where the car will move to, and move() that actually moves it to the new location.

I wrote some part of movement in main() function:

1
2
3
4
float x, x1, y, y1, dir, dir1;
waypoints[0].getPosition(x, y, dir);
nextwaypoint = waypoints[0].getNext();
waypoints[nextwaypoint].getPosition(x1, y1, dir1);


Then, I located the car at x1, y1, dir1 as:

1
2
Car car(CAR1, x1, y1, dir1);
car.draw(&window); //window->draw(sprite); 


And, I wrote this to get all waypoint's position and their next's:

1
2
3
4
5
6
7
for(int i = 0; i < 32; i++) {
    waypoints[i].getPosition(x, y, dir);
    nextwaypoint = waypoints[i].getNext();
    waypoints[nextwaypoint ].getPosition(x, y, dir);
    this->sprite.setPosition(sf::Vector2f(x, y));
    this->sprite.setRotation(dir * 90);
}


But this code just locate the car at the last waypoint. I do not add an update point as you mentioned in nextPos() function.

Thank you again for all these improvements on the code and your guidance.
Topic archived. No new replies allowed.