I have a piece of code which I am doubting it as a implementation of recursion by its definition. My understanding is that the code must call itself, the exact same function. I also question whether writing the code this way adds additional overhead which can be seen with the use of recursion. What are your thoughts?
class dhObject
{
public:
dhObject** children;
int numChildren;
GLdouble linkLength; //ai
GLdouble theta; //angle of rot about the z axis
GLdouble twist; //about the x axis
GLdouble displacement; // displacement from the end point of prev along z
GLdouble thetaMax;
GLdouble thetaMin;
GLdouble thetaInc;
GLdouble direction;
dhObject(ifstream &fin)
{
fin >> numChildren >> linkLength >> theta >> twist >> displacement >> thetaMax >> thetaMin;
//std::cout << numChildren << std::endl;
direction = 1;
thetaInc = 1.0;
if (numChildren > 0)
{
children = new dhObject*[numChildren];
for(int i = 0; i < numChildren; ++i)
{
children[i] = new dhObject(fin);
}
}
}
void traverse(void)
{
glPushMatrix();
//draw move initial and draw
transform();
draw();
//draw children
for(int i = 0; i < numChildren; ++i)
{
children[i]->traverse();
}
glPopMatrix();
}
void update(void)
{
//Update the animation, if it has finished all animation go backwards
if (theta <= thetaMin)
{
thetaInc = 1.0;
} else if (theta >= thetaMax)
{
thetaInc = -1.0;
}
theta += thetaInc;
//std::cout << thetaMin << " " << theta << " " << thetaMax << std::endl;
for(int i = 0; i < numChildren; ++i)
{
children[i]->update();
}
}
void draw(void)
{
glPushMatrix();
glColor3f (0.0f,0.0f,1.0f);
glutSolidCube(0.1);
glPopMatrix();
}
void transform(void)
{
//Move in the correct way, R, T, T, R
glRotatef(theta, 0, 0, 1.0);
glTranslatef(0,0,displacement);
glTranslatef(linkLength, 0,0);
glRotatef(twist, 1.0,0.0,0.0);
}
};
This is a matter of definition/nitpicking. In this C function:
void traverse( tree * t ) {
if ( t != 0 ) {
traverse( t->right );
traverse( t->left );
}
}
Is the function recursive? I would say yes, even though it is being called on different objects. So I would say your code is also recursive. To take an even more extreme example:
unsigned int f( unsigned int n ) {
if ( n = 0 ) {
return 0;
}
else {
return f( n - 1 ); // XXX
}
}
The thing the function is being called on at XXX is obviously not the same thing it was originally called on. But I think everyone would agree this is a recursive function.
Yes, since you have certain functions calling themselves. By definition that is direct recursion. You could also have indirect recursion if you had function A() calling function B(), function B() in turn (directly or indirectly) calling function A() again.
Looks like recursion in the traverse() and update() methods with depth controlled by the physical geometry of your object collection.
If this is your actual class I would recommend a few things:
Check the upper bound of numChildren before using it lest someone passes in a remarkably huge number by mistake.
If this will be used in a threaded environment you might want to synchronize access to the child objects.
Consider using containers instead of allocating an array. I don't see a destructor either so you'd leak the memory for the array storage and the children if this object gets deleted.
If all you're worried about is the overhead of recursion then the important thing is to measure how deep your stack goes. It doesn't matter whether you're working recursively or not, if your stack is 100 calls deep.
Calling one of these methods(traverse or update) will have the effect of calling that same method an every child. So, the methods are not recursive. Instead, it's a recursive algorithm: it is applied recursively on the logical tree of objects.
The depth of the call stack is directly determined by the structure of the data on which the algorithm operates.
What really happens is this(pseudo code):
Function Traverse(object o)
{
[do something with o]
Foreach(object child in o.Children)
Traverse(child);
[do something with o]
}
Related
My problem is that when trying to add to linked lists within an array it would seem to not work and I'm almost totally ignorant as to why this is. First I declare the object move as seen below:
struct move {
move(int startX, int startY, int endX, int endY)
{
this->startX = startX;
this->startY = startY;
this->endX = endX;
this->endY = endY;
this->next = nullptr;
}
move()
{
next = nullptr;
this->startX = -1;
}
int startX;
int startY;
int endX;
int endY;
move* next;
};
I then declare 2 arrays, one which contains 100 linked lists of the moveobject and another which contains pointers to elements in each of the linked lists in the first array. As seen below:
move possibleMoves[100];
move * endOfLists[100];
I then initialise these arrays as seen below:
for (int i = 0; i < 100; i++) {
possibleMoves[i] = move();
endOfLists[i] = &possibleMoves[i];
}
Moving onto the function itself which adds to one of the linked lists within the possibleMoves array I prototype it as such:
void listAdd(move * list, move * object, int width);
I call it as such:
if (possibleMoves[0].startX == -1) {
possibleMoves[0] = *(new move(x, y, x + xOffset, y + yOffset));
}else {
listAdd(endOfLists[width], new move(x, y, x + xOffset, y + yOffset), width);
}
And the function is declared as such:
void listAdd(move * list, move * object, int width) {
int count = 0;
while (list->next != nullptr){
count++;
list = (*list).next;
}
std::cout << "\nCount: " << count << std::endl;
list->next = object;
endOfLists[width] = list->next;
}
Count always outputs as '0'.
Here is a link to all the code (https://pastebin.com/E5g58N6L) it's not pretty. The listAdd procedure is called on lines 188, 197 and 444. Here is the MCVE:
#include<stdio.h>
#include <stdlib.h>
#include<math.h>
#include<iostream>
struct move {
move(int startX, int startY, int endX, int endY)
{
this->startX = startX;
this->startY = startY;
this->endX = endX;
this->endY = endY;
this->next = nullptr;
}
move()
{
next = nullptr;
this->startX = -1;
}
int startX;
int startY;
int endX;
int endY;
move* next;
};
void listAdd(move * list, move * object, int width);
move possibleMoves[100];
move * endOfLists[100];
int main() {
int x = 0;
int y = 0;
int xOffset = 1;
int yOffset = 1;
int width = 0;
for (int i = 0; i < 100; i++) {
possibleMoves[i] = move();
endOfLists[i] = &possibleMoves[i];
}
if (possibleMoves[0].startX == -1) {
possibleMoves[0] = *(new move(x, y, x + xOffset, y + yOffset));
}else {
listAdd(endOfLists[width], new move(x, y, x + xOffset, y + yOffset), width);
}
void listAdd(move * list, move * object, int width) {
int count = 0;
while (list->next != nullptr)
{
count++;
list = (*list).next; //Go down the list until it reaches an item with nothing next.
}
std::cout << "\nCount: " << count << std::endl;
list->next = object;
endOfLists[width] = list->next;
}
NM the full code. You don't tell us what the initial width is, but assuming it's valid (that is -1<width<100), it doesn't matter. Look at what happens here (end of add function):
list->next = object;
At this point list is endOfLists[width]. Now, you properly set the next to the new object, so far so good.
But what now?
endOfLists[width] = list->next;
So the "head" pointer, saved in the global (why?) is bypassing what it originally pointed to (who has a proper next!) and points directly to a descendant, the new object with NULL next. I'm guessing this wasn't what you want:
Leaking memory 0 - at first static allocation, but this can quickly be dynamic if you call this function the way you did a lot.
The head pointer is still, at the end of the function, an object with NULL next. A new one indeed, but the next is still NULL - so the next call will perform the same actions.
Basically you're swapping the head pointers, while leaking memories. You want to either:
Set the object next first: object->next=endOfLists[width], and set the second line to let endOfLists[width]=object, kind of reversing the list.
Delete the second line, and leave the original head.
Also:
I can't see a good reason for globals
Why is the add function not a move method?
You need to make sure you delete all those new objects.
EDIT
I saw your addition - the first call will always return 0, since all initial objects have NULL nexts, even if you fix your code. You need two calls to the same index (width) at least twice to start testing this.
Graphical addendum
At the beginning we have 100 objects, stored in an array, all looking like this:
head->list->NULL
list here is the object pointed to by endOfLists at some index, and we call that pointer head. Now, we want to add the new object. We enter the addition function, with the first argument endOfLists[width], so this will be our list argument in the function itself.
We immediately skip the while (since our next is already NULL). Getting to the first line above, we now connect our head to the new object:
list->object->NULL
So in the array we have:
head->list->object->NULL
Again, head is the pointer stored in endOfLists[width]. Now we tell endOfLists[width] to swap the head to a different one, setting it equal to list->next, which is object. So how does our memory look like?
head->object->NULL
list->^
Both head (the array cell) and list point to object, and nothing points to list. There we go leaking. Next time we call the function with the updated cell, we will repeat the process, leaking object:
head-> object2->NULL
list->object->^
and so forth.
So I'm writing a program which uses a double pointer in some somewhat deep recursion, and I've got a huge memory leak which I can't find the source of. The way the program works is (and I'll post some sample code below) that there is a board object with a double pointer called "board" which is the real board (it's a connect 4 game) and another double pointer called "newBoard" which is what is used by a function called minimax() (outside of the Board class).
minimax() is recursive, stepping through each possible route the game could take, and since I don't want to edit the actual board at all, and I figured copying the entire "board" object thousands and thousands of times would be unncecessary, I thought that creating this "newBoard" pointer to copy the actual board would be the best option.
I have a function to delete newBoard, but it's not working like I'd like it to. Here are the relevant pieces of code:
minimax():
void Brain::minimax(Board board, int who, int currentCheck, int iter)
{
board.createNewBoard();
if (iter <= MAX_ITER)
{
for (int i = 0; i < w_; i++)
{
if (board.playMove(i, currentCheck))
{
if (winDetect(board, board.getDisc('c')))
{
if (iter == 0)
{
score[i] += 1000;
}
else
score[i] += (MAX_ITER - iter);
}
else if (winDetect(board, board.getDisc('p')))
{
if (iter == 1)
{
score[i] += 500;
}
else
score[i] -= (MAX_ITER - iter);
}
else if (!winDetect(board, board.getDisc('c')) && !winDetect(board, board.getDisc('p')))
{
if (currentCheck == board.getDisc('p'))
currentCheck = board.getDisc('c');
else if (currentCheck == board.getDisc('c'))
currentCheck = board.getDisc('p');
minimax(board, who, currentCheck, iter + 1);
}
}
}
}
}
createNewBoard():
void Board::createNewBoard()
{
newBoard = new int*[h_];
for (int i = 0; i < h_; i++)
newBoard[i] = new int[w_];
}
NB_delete():
void Board::NB_delete()
{
for (int i = 0; i < w_; i++)
delete[] newBoard[i];
delete[] newBoard;
}
I think that's all the relevant code, but if you think there might be more, let me know and I'll include it! Thanks in advance for any help.
EDIT: SOLVED
I had simply forgotten to call my NB_delete() function. Calling it fixed the program!
As far as I can see, newBoard is a member of class Board. Now minimax is called recursively and so is CreateNewBoard. That means: In CreateNewBoard you are overwriting newBoard in each successive recursive call! That's probably your memory leak.
If CreateNewBoard is to be called recursively, you need a separate instance of newBoard for each level of recursion. Or is newBoard a 2D array for that very reason and the h_ index denotes recursion depth? In that case you should not create a new instance of newBoard in iteration 2 ... h_.
I'm building a sparse matrix class that holds two arrays (row and column) of pointers to doubly linked lists (down and right). Sort of like this:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
Both arrays are initialized to have nullptr in every space until something is inserted in that place.
I have a function "readFile" that reads in objects from a text file and inserts them into this sparse matrix. For some reason, before this function returns, all of the data in it is fine, but after I return, I get random memory locations in my arrays. Here is main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "sparseMatrix.h"
using namespace std;
class basic
{
private:
int x, y;
string word;
basic *down;
basic *right;
public:
basic(int x, int y, string word)
{
this->x = x;
this->y = y;
this->word = word;
down = nullptr;
right = nullptr;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
basic *getRight()
{
return right;
}
void setRight(basic *newRight)
{
right = newRight;
}
basic *getDown()
{
return down;
}
void setDown(basic *newDown)
{
down = newDown;
}
void print()
{
cout << "X: " << x << ", Y: " << y << ", word: " << word << ".\n";
}
};
sparseMatrix<basic> readFileBROKEN(string pathToFile);
sparseMatrix<basic> *readFile(string pathToFile);
int main()
{
cout << "Working:\n\n";
sparseMatrix<basic> *workingMatrix = readFile("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
workingMatrix->printyArray();
cin.get();
cout << "Not working:\n\n";
sparseMatrix<basic> brokenMatrix = readFileBROKEN("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
brokenMatrix.printyArray();
cin.get();
delete workingMatrix;
}
sparseMatrix<basic> readFileBROKEN(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> matrix(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix.insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix.printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
sparseMatrix<basic> *readFile(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> *matrix = new sparseMatrix<basic>(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix->insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix->printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
and here is sparseMatrix.h:
template <class dataType>
class sparseMatrix
{
private:
//The dimensions of the sparse matrix.
int width;
int height;
//Dynamic array of pointers to heads of linked lists.
dataType** xArray;
dataType** yArray;
public:
//Constructor. Sets everything in the two arrays to nullptr.
sparseMatrix(int height, int width)
{
this->width = width;
this->height = height;
xArray = new dataType*[width];
yArray = new dataType*[height];
for (int row = 0; row < height; row++)
{
this->yArray[row] = nullptr;
}
for (int col = 0; col < width; col++)
{
this->xArray[col] = nullptr;
}
}
//Deconstructor. First goes through the matrix and looks for every city it can find, and deletes
//all of those. Then when it's done, it deletes the two dynamic arrays.
~sparseMatrix()
{
dataType *currentdataType;
dataType *next;
for (int row = 0; row < height; row++)
{
currentdataType = yArray[row];
while (currentdataType != nullptr)
{
next = currentdataType->getRight();
delete currentdataType;
currentdataType = next;
}
}
delete [] yArray;
delete [] xArray;
}
//Creates a copy of the data we are passed, then creates links to this copy.
void insert(dataType data)
{
//Make sure the data is valid.
if (data.getX() < 0 || data.getX() >= width || data.getY() < 0 || data.getY() >= height)
{
std::cout << "That dataType doesn't fit into the sparse matrix!\n";
data.print();
std::cin.get();
}
else
{
//Copy the data we were passed.
dataType *newData = new dataType(data);
//Easy case. If nothing is in this row, set yArray[row] to the address of this data.
if (yArray[data.getY()] == nullptr)
{
yArray[data.getY()] = newData;
}
//Not so easy case. Move forward (right) until we find the right location, then set links.
else
{
dataType *current = yArray[data.getY()];
while (current->getRight() != nullptr)
{
current = current->getRight();
}
current->setRight(newData);
}
//Easy case. If nothing is in this col, set xArray[col] to the address of this data.
if (xArray[data.getX()] == nullptr)
{
xArray[data.getX()] = newData;
}
//Not so easy case. Move forward (down) until we find the right location, then set links.
else
{
dataType *current = xArray[data.getX()];
while (current->getDown() != nullptr)
{
current = current->getDown();
}
current->setDown(newData);
}
}
}
void printyArray()
{
for (int r = 0; r < height; r++)
{
if (yArray[r] != nullptr)
{
std::cout << r << ' ';
//yArray[r]->print();
}
}
}
};
readFile reads everything in from a file that looks like this:
0 0 hello
5 2 world
6 8 foo
9 5 bar
...
As expected, before returning, the only locations that are NOT nullptr are the ones that I have inserted into. (0, 2, 8 and 5). However when the function returns, EVERY SINGLE location in the array is not nullptr. I added a second function which returns a pointer to dynamically allocated sparseMatrix object, rather then returning the object itself, and this fixed it. However, I don't understand why. It seems like these two functions should behave identically the same way.
Also, the part that is most confusing to me, why does this run perfectly fine in Xcode, but not in Visual Studio?
tomse's answer is correct and gives the why and a fix, but it's an unnecessarily expensive fix for this problem. His suggestion of the copy constructor also solves numerous future problems such as the classics Why did my vector eat my data? and Dude, where's my segfault? Make the copy constructor. Don't use it unless you have to.
I think Andras Fekete got the problem right, but his post is kind of garbled. His solution is bang on, though.
Define your function like this:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
Remove the definition of matrix inside the function in favour of the one passed in.
Return false on error so you know the matrix is bad (or use exceptions).
Create the matrix in the calling function and pass it into the revised reader function.
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
That puts you right back where you were with the pointer version, but without the pointer and without having to do the extra work of copying data you didn't need to copy.
Your function:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns a copy of the object (which is OK), but sparseMatrix does not define a copy constructor, so the default generated will be used which creates a shallow copy by just copying the adresses inside the returned object.
But the memory where the address points to is deleted when you leave your function (because the destructor of the locally created object is called).
To solve this you have to define your own copy contructor in sparseMatrix which copies all the content of the object.
sparseMatrix(const sparseMatrix& rhs) :
width(rhs.width),
height(rhs.height),
xArray(nullptr),
yArray(nullptr)
{
... and now copy all the content from rhs.xArray to this->xArray,
(same for yArray)
}
The problem is that you're allocating 'matrix' inside both of the readFile functions. Upon returning from the function, both variables are deallocated. However, returning the value (eradFile) the matrix is copied into your variable of the calling function, whereas returning the pointer (readFileBROKEN) is just returning the address where the matrix used to be stored.
To fix this, you should allocate the 'matrix' variable, and pass in a reference to the function. Then the function can return a void while stuffing the matrix properly.
I have a Base class Point (representing a 2D point in space) that is non-thread-safe for move operations; so I defined an inherited class LockedPoint that overrides 2 methods in the base class: moveTo and moveBy:
void Point::moveTo(float xPos, float yPos) {
x = xPos;
y = yPos;
}
void Point::moveBy(float xOff, float yOff) {
x += xOff;
y += yOff;
}
void LockedPoint::moveTo(float xPos, float yPos) {
MutGuard m(lock);
x = xPos;
y = yPos;
}
void LockedPoint::moveBy(float xOff, float yOff) {
MutGuard m(lock);
x += xOff;
y += yOff;
}
( where x and y = private member variables,
lock = a private mutex, and
MutGuard = typedef lock_guard<mutex> )
To visually see the problem with the "unlocked" Point, I wrote up a test routine:
void sleepForMS(long ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
void messWithPoint(Point& p, int type) {
float n = 1;
if (type) n *= -1;
for (long i = 0; i < 10000; i++) {
p.moveBy(n, n);
sleepForMS(rand() % (type ? 2 : 3));
if (i % 500 == 0)
std::cout << i << ":\t" << p << std::endl;
}
}
int main(int argc, char* argv[]) {
using namespace std;
Point p;
thread t1(messWithPoint, std::ref(p), 0);
sleepForMS(33);
thread t2(messWithPoint, std::ref(p), 1);
cout << "Started\n";
t1.join();
t2.join();
cout << p << endl;
}
With a Point, the resulting p is "corrupted", as expected (it should equal (0,0) by the end, and it doesn't). If I change p to a LockedPoint though, the base version of moveBy is still called (verified by print debugging).
I read up on method "overriding" (apparently more correctly called "method hiding"), and from what I understand, if the overriding method has the same signature as the base method, it hides the base version, and is called instead. Why then is the base method being called despite the 2 having the same signature? The only thing I can think of is because I'm specifying Point in messWithPoint's argument list, it's taking that literally and calling Point's version. If I change the signature to void messWithPoint(LockedPoint& p, int type), the resulting LockedPoint is (0,0), as expected. Shouldn't it "see" that the passed LockedPoint overrides the used method, and use the "least hidden" version?
If that's not how it works, is there a way to specify taking the base class, but having it use any available overridden versions?
The member functions are not virtual, so the functions in the class known at compile time are used.
However, for a simple class such as point, using virtual member functions or providing automatic mutual exclusion goes against the C++ idea of not paying for what you don't use.
Just copy points.
I am making an asteroids game in C++ using SFML. I seem to have a problem though with shooting bullets. Although the class seems to work each time a bullet is shot the game significantly slows down. This is the code for the spaceship and the bullets. I just can't seem to find what's wrong with it! Thank you for your time.
This is the Code of the Ship:
#include "Spaceship.h"
Spaceship::Spaceship(void){}
Spaceship::~Spaceship(void){}
void Spaceship::LoadResources()
{
if(!shipAnimImg.LoadFromFile("Resources/Images/SpaceshipAnimation.png"))
std::cout <<"Could not locate the ship animation image" <<std::endl;
if(!shipIdleImg.LoadFromFile("Resources/Images/SpaceshipIdle.png"))
std::cout <<"Could not locate the ship idle image" <<std::endl;
if(!bulletImg.LoadFromFile("Resources/Images/Bullet.png"))
std::cout <<"Could not locate the bullet image" <<std::endl;
shipSpr.SetImage(shipIdleImg);
shipSpr.SetScale(0.5,0.5);
shipSpr.SetCenter(shipIdleImg.GetWidth() / 2,shipIdleImg.GetHeight() / 2);
x = DEFAULT_SCREENWIDTH / 2;
y = DEFAULT_SCREENHEIGHT / 2;
shipSpr.SetPosition(x,y);
shipSpr.SetRotation(90);
std::cout<<shipSpr.GetCenter().x<<std::endl;
std::cout<<shipSpr.GetCenter().y<<std::endl;
vx = 0.2;
vy = 0.2;
isBulletOnScreen = false;
isPressed = false;
}
void Spaceship::UnloadResources(){}
void Spaceship::Update(sf::RenderWindow &Window,sf::Event event)
{
if (Window.GetInput().IsKeyDown(sf::Key::A))
{
shipSpr.Rotate(0.08);
}
if (Window.GetInput().IsKeyDown(sf::Key::D))
{
shipSpr.Rotate(-0.08);
}
if (Window.GetInput().IsKeyDown(sf::Key::W))
{
x += (cos(shipSpr.GetRotation() * (3.14159265/180.0)) *0.2);
y -= (sin(shipSpr.GetRotation() * (3.14159265/180.0)) *0.2);
shipSpr.SetPosition(x,y);
}
if (Window.GetInput().IsKeyDown(sf::Key::Space) && !isPressed)
{
isBulletOnScreen = true;
isPressed = true;
bullets.push_back(new Bullet(shipSpr.GetPosition().x,shipSpr.GetPosition().y,0.3,shipSpr.GetRotation(),bulletImg));
}
if (event.Type == sf::Event::KeyReleased)
{
isPressed = false;
}
if(bullets.size() != 0)
{
for (int i=0; i<bullets.size(); i++)
{
bullets[i]->Update(Window,event);
if ((bullets[i]->GetX() > DEFAULT_SCREENWIDTH + 40) || (bullets[i]->GetX() < 0 - 40) ||
(bullets[i]->GetY() > DEFAULT_SCREENWIDTH + 40) || (bullets[i]->GetY() < 0 - 40))
{
bullets.erase(bullets.begin() +i);
}
}
std::cout<<bullets.size()<<std::endl;
}
std::cout<<bullets.size()<<std::endl;
}
void Spaceship::Draw(sf::RenderWindow &Window)
{
if(isBulletOnScreen)
for (int i=0; i<bullets.size(); i++)
{
Bullet *cur = bullets[i];
bullets[i]->Draw(Window);
std::cout<<bullets.size()<<std::endl;
}
Window.Draw(shipSpr);
}
And this is for the Bullet:
#include "Bullet.h"
Bullet::Bullet(void){}
Bullet::Bullet(float x,float y,float v,float r,sf::Image image)
{
LoadResources(x,y,v,r,image);
}
Bullet::~Bullet(void){}
void Bullet::LoadResources(float x,float y,float v,float r , sf::Image image)
{
this->x = x;
this->y = y;
this->v = v;
bulletImg = image;
bulletSpr.SetImage(bulletImg);
bulletSpr.SetScale(0.5,0.5);
bulletSpr.SetCenter(bulletImg.GetWidth() / 2,bulletImg.GetHeight() / 2);
bulletSpr.SetPosition(x,y);
bulletSpr.SetRotation(r);
}
void Bullet::UnloadResources(){}
void Bullet::Update(sf::RenderWindow &Window,sf::Event event)
{
x += (cos(bulletSpr.GetRotation() * (3.14159265/180.0)) *v);
y -= (sin(bulletSpr.GetRotation() * (3.14159265/180.0)) *v);
bulletSpr.SetPosition(x,y);
}
void Bullet::SetX(float x)
{
this->x = x;
}
void Bullet::SetY(float y)
{
this->y = y;
}
void Bullet::SetRotation(float r)
{
rotation = r;
}
float Bullet::GetX()
{
return x;
}
float Bullet::GetY()
{
return y;
}
void Bullet::Draw(sf::RenderWindow &Window)
{
Window.Draw(bulletSpr);
}
EDIT: Changed the code so that it loads the image inside the Spaceship Class and passes it to the Bullet's Resources after it is created. The problem still remains the same though. The game becomes more and more slower each time a bullet is shot and it remains slow until it is erased.
1.
You are loading the Bullet PNG image from disk every time you create a new object (often, if you like shooting). The loading from disk is probably going to be very slow. Try to reuse the same image several times instead!
You could probably pull the LoadFromFile function out of LoadResources and put it somewhere where it would last for the duration of the game. Then just have LoadResources refer to that place whenever a new bullet needs to be created. The same goes for any other images or resources that can be reused in your game.
2.
I also saw that you have std::cout in your code. Try removing all of those that are in the rendering loop, as printing is slow.
for (int i=0; i<bullets.size(); i++)
{
Bullet *cur = bullets[i];
bullets[i]->Draw(Window);
std::cout<<bullets.size()<<std::endl; // This is going to be slow
}
3.
You might also want to look at the bullets vector. When adding bullets to it,push_back changes the size of the vector, and that takes some allocation and deallocation of memory each time. A better approach would be to make room for a maximum number of bullets to start with (using the resize function for example) so that the vector doesn't change size whenever a bullet is created.
if (Window.GetInput().IsKeyDown(sf::Key::Space) && !isPressed)
{
isBulletOnScreen = true;
isPressed = true;
bullets.push_back(new Bullet(...); // avoid this
}
Everytime you call a new Bullet
Bullet::Bullet(float x,float y,float v,float r)
{
LoadResources(x,y,v,r);
}
You also call LoadResources(x,y,v,r) which calls
bulletImg.LoadFromFile("Resources/Images/Bullet.png")
and that call read a file from a disk, which is a very slow operation, in order of magnitudes slower than anything else, so your program stops for the duration of the load.