So I am having a small issue with creating a list and having it write to my window using the SFML library. Basically what happens is that when I execute the fire() function the game freezes & crashes.
Variable code:
sf::Sprite laser;
The list code is:
std::list<sf::Sprite>laserList;
The fire code is (called when I click button):
bool fire(void)
{
laserList.push_back(laser);
return true;
}
This is the drawing code to window:
//This is code for running through and drawing each sprite in list
for(std::list<sf::Sprite>::iterator it = laserList.begin(); it != laserList.end(); laserList;)
{
app.draw(*it);
}
Your for loop is a bit strange here:
for(std::list<sf::Sprite>::iterator it = laserList.begin(); it != laserList.end(); laserList;)
You'll find that this will form an infinite loop as the value of it never changes. This may be what leads to your crash. To fix it you want to increment the iterator after every loop:
for(std::list<sf::Sprite>::iterator it = laserList.begin(); it != laserList.end(); ++laserList)
There was an edit that suggested the following but was rejected for some reason. I have added it here for completeness
You could avoid any confusion over this syntax by using the Range Based for loop introduced in C++11:
for(sf::Sprite spr : laserList){
app.draw(spr);
}
Related
I am trying to make my simple game and I came across small issue. So I have 2 vectors of pointers.
One is for bullets and one is for enemy units.
First I tried to iterate through all of my bullets and then in second loop iterate through my enemies and when I find collision I erase enemy unit and bullet, but erasing bullets is crashing my game, so I figured out that I shouldnt erase from vector while its still iterating in first loop.
My second ide was something like this:
std::vector<Bullet*>::iterator remove = std::remove_if(bullets.begin(), bullets.end(),
[&](Bullet* x)
{
for (std::vector<EnemyUnit*>::iterator it = enemyUnits.begin(); it != enemyUnits.end(); ++it)
{
if (x->collision->CheckCollision((*it)->collision))
{
enemyUnits.erase(it);
return true;
}
else
{
return false;
}
}
});
bullets.erase(remove, bullets.end());
So it seems to work in a way, but my bullets can only collide with one enemy at a time and it seems like its not checking for all enemies. For example I shoot 5 bullets and firstly they can only collide with one enemy and after one bullet kills enemy other bullets will be able to collide with second enemy and so on . It seems like
for (std::vector<EnemyUnit*>::iterator it = enemyUnits.begin(); it != enemyUnits.end(); ++it)
Doesnt take place and it only gets one enemy? Is it wrong way to do it?
I need to use pointers and vectors, because its for my project.
The predicate for std::remove_if returns after the first check, no matter what.
To fix this, the predicate can return on a collision, or at the end (after the loop) on no collision, e.g.
for (auto it = enemyUnits.begin(); it != enemyUnits.end(); ++it)
{
if (x->collision->CheckCollision((*it)->collision))
{
enemyUnits.erase(it);
return true;
}
}
return false;
This way, if a collision is detected, it will exit early and return true.
If there's no collision with any enemy, it will stop after the loop and return false.
Answer advising to remove the return statements is correct. The return statements are exiting the inside loop early, but that isn't the only problem. Removing those returns will reveal the next problem. You need:
it = enemyUnits.erase(it);
to correctly set the iterator after an erase to prepare for the next pass through the loop.
In my project, I have a QButtonGroup with 256 pushbuttons in it. I also gave each button an id like this:
void MainWindow::AddBlocksToGroup()
{
QButtonGroup* blockGroup = new QButtonGroup(this);
blockGroup->addButton(ui->Oblock_0, 0);
...
blockGroup->addButton(ui->Oblock_255, 255);
}
I am trying to loop through all the buttons in the group and change their text but I keep getting errors when my program reaches the part where I loop through the buttons. This is what I currently have to loop through them:
for(int i = 0; i <= 255; i++)
{
blockGroup->button(i)->setText("Test"); //Read access violation?
}
I always get a read access violation in my loop when my program reaches this point. Why is this?
Thanks for your time.
I would do this for the iteration code:
foreach(QAbstractButton *button, blockGroup->buttons()) {
button->setText("Test");
}
If this still gives you crashes, then there's something else going on in your program that is invalidating those button pointers.
I know it has been a long time, you probably have already solved this. If not, maybe you need to check whether blockGroup->buttons() return value is an empty list or not. If it is an empty list, then your program crashes.
You seem to be creating a local variable called blockGroup in your AddBlocksToGroup() function. Perhaps what you are trying to do is initialize your MainWindow member variable that uses the same name?
So instead of: QButtonGroup* blockGroup = new QButtonGroup(this);
you should do: blockGroup = new QButtonGroup(this);
I am making a simple game in SFML 2 and it came smoothly so far. I created two sf::Sprite lists, one for enemies and one for lasers. The enemies spawn randomly off-screen and the lasers are created whenever input is given. I created a collision loop for both the lists and executed my code. There are no compile time and run time errors. The laser-enemy collision works fine for the first 3 to 4 enemies but after that, the collision does not occur. What might be causing this problem? Please help me on this. Thanks. Here's my code.
std::list<sf::Sprite>::iterator enemyit = enemy.begin(), next;
std::list<sf::Sprite>::iterator greenlaserit = greenlaser.begin(), reload;
while(enemyit != enemy.end())
{
next = enemyit;
next++;
while(greenlaserit != greenlaser.end())
{
reload = greenlaserit;
reload++;
if(enemyit->getGlobalBounds().intersects(greenlaserit->getGlobalBounds()))
{
enemy.erase(enemyit);
greenlaser.erase(greenlaserit);
++erased;
}
greenlaserit = reload;
}
enemyit = next;
}
It seems to be that you are doing a lot of iterator manipulation and that is likely to be where the problem is occurring.
If you can use c++11, I would suggest looking into the for each loop (http://www.cprogramming.com/c++11/c++11-ranged-for-loop.html), to keep things really simple to read and understand (and thus, easier to debug).
You could do something like this:
std::list<sf::Sprite> enemies;
std::list<sf::Sprite> lasers;
for (sf::Sprite enemy: enemies) {
for (sf::Sprite laser : lasers) {
if (enemy.getGlobalBounds().intersects(laser.getGlobalBounds())) {
enemies.remove(enemy);
lasers.remove(laser);
}
}
}
Edit: otherwise, one method I have found for figuring out an iterator problem is stepping through it by hand. I draw two rectangles with cells for each location, and keep track of the iterators and run through the logic step by step. Before each iteration of your logic, write down your expected results. Then go through it by hand and see if your results match your expectations.
I know that in order to kill invaders in C++, I need to make a collider.
However, nothing will ever kill the invaders in that game.
Here's the code in the header:
bool DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight);
This is the function I'm initializing:
bool Game::DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight) {
if (Xbpos+BulWidth < Xipos || Xbpos > Xipos+InvWidth) return false;
if (Ybpos+BulHeight < Yipos || Ybpos > Yipos+InvHeight) return false;
return true;
}
And this is what happens if somebody presses the space key:
if (code == 57) { //Space
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[counter].Active = false;
printf("Collide!\n");
}
}
Does anybody know what's going wrong?
The problem isn't C++. The problem is how you are using it. The only way you'll get a kill with your code as written is if the invader is right on top of you. But that's too late. The alien invader has already killed you.
What you need to do is make those bullets into objects that you propagate over time, just like your invaders are objects that you propagate over time. The response to the user pressing a space key should be to add a new instance of a bullet to the set of active bullets. Each of those active bullets has a position that changes with time. On each time step, you should advance the states of the active invaders per the rules that dictate how invaders move and advance the states of the active bullets per the rules that dictate how bullets move. Remove bullets when they reach the top of the screen, and if an alien invader reaches the bottom of the screen, game over.
After propagating, removing off-screen bullets, and checking for game over, you want to check for collisions between each of the N bullets with each of the M invaders. When a collision is detected, remove the bullet from the set of active bullets and delete the alien invader from the set of active invaders. And of course you'll want some nifty graphics to show the user that another alien bit the dust.
Aside: Being an NxM problem, this check might be the biggest drain on CPU usage. You can speed this up with some simple heuristics.
You could manage the collections of alien invaders and bullets yourself, carefully using new and delete so as to prevent your invaders and bullets from killing your program with a memory leak. You don't have to do this. C++ gives you some nifty tools to manage these collections. Use one of the C++ standard library collections instead of rolling your own collection. For example, std::vector<AlienInvader> invaders; or std::list<AlienInvader> invaders, and the same for bullets. You'll be deleting from the middle a lot, which suggests that std::list or std::deque might be more appropriate than std::vector here.
You test the collision for the fired item just when they are created
Shouldn't be the test collision done in the main loop for each existing item at each frame ?
Don't worry, C++ has got all you need to kill invaders :)))
It's not easy to give advice based on so little code, but here the only logical error seems to be you test for collision only when space is pressed; you should test for it in an outside loop probably:
if (code == 57) { //Space
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
}
From a logical point of view, pressing Space should fire a bullet: the starting position for the bullet is set, and so is its speed on the Y axis (so that it goes up).
The code that check for collision should go outside of this if block. In fact, this block of code is executed only if you're still pressing space -that is: still firing-. Should collision be checked only if you're "still firing"? Do the fact that you fired a bullet and started waiting for it to destroy the invader interfere in some way with the fact that this bullet can reach the invader and, indeed, destroy it? Of course not!
if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[counter].Active = false;
printf("Collide!\n");
}
You want collision to be checked in an outside loop, the same that probably also contains the checks for key presses. In this way, even if you're just looking at the screen and waiting, the program keeps testing the condition and, when it's fulfilled, code associated with the event of collision is executed (that is: an invader is "inactivated").
You say //Space , is that what it is or should it be 32 (if ASCII) instead of 57? Does the program flow into the if==57 block?
Your code looks fine, but you need two loops around the collision checker: one for checking all invaders (not just one of them) and another one to check at every bullet position along its trajectory, not just the moment when it leaves the gun.
I will assume we have an auxiliary function that moves the bullet and returns whether it is still inside the screen:
bool BulletIsInScreen();
Then we can write the loops:
if (code == 57) { // Space
while (BulletIsInScreen()) {
for (size_t i = 0; i < counter; ++i) { // counter is the number of invaders,
// according to your comment to your own answer
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
if (DoCollision(Invaders[i].MyBullet.Xbpos, Invaders[i].MyBullet.Ybpos,
Invaders[i].MyBullet.BulWidth, Invaders[i].MyBullet.BulHeight,
Invaders[i].Xipos, Invaders[i].Yipos,
Invaders[i].InvWidth, Invaders[i].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[i].Active = false;
printf("Collide!\n");
}
}
}
}
Now this should work as expected.
Here is my code for checking if future move is legal, I have assumed its legal and copied move into mySquares array. I then call this method in the game cycle set in the form and in the timer handler which is:
canvas->drawGrid();
testBlock->drawBlock();
testBlock->moveDown();//this method has checkBounds for when hit sides, top & bottom
if(newBlock->canMoveDown()==false)
{
newBlock->addMySelfToGameBoard();
mainGameBoard->updateGrid();
}
//timer1 handler finish
bool TTetrisBlock::canMoveDown()
{
array<Point>^ temporaryCopy = gcnew array<Point>(4);
bool canGoDown = true;
for(int i=0;i<mySquares->Length;i++)
{
//Set future move
temporaryCopy[i].X = mySquares[i].X;
temporaryCopy[i].Y = mySquares[i].Y+1;
}
//Check if future move cells are full, if not assign values to mySquares
//Check if future move is legal
for(int j=0;j<temporaryCopy->Length;j++)
{
if(gameBoard->isCellOccupied(temporaryCopy[j].X,temporaryCopy[j].Y) == true)
{
mySquares[j].X = temporaryCopy[j].X;
mySquares[j].Y = temporaryCopy[j].Y;
}
}
return canGoDown;
}
//end of moveDown
in my gameboard class i have the method which checks if TCell is occupied or not. TGameBoar holds an array of TCells which has a color and bool isOccupied = false;
bool TGameBoard::isCellOccupied(int c,int r)
{
//Checks if TCell is occupied
return myGrid[c,r]->getIsOccupied();
}
It Crashes and indicates here was the problem, Im currently learning C++ at school. I would appreciate some help. I am also struggling with the Keydown for moving left and right using e->KeyData == Keys::Left) etc. and creating a newblock when gone through loop.
I have my project rar if you want to check it out. I have all the classes done, its just putting it together is the hard bit.
Project Tetris
I see three problems.
First you should only move mySquares when isCellOccupied returns false (not true as you currently have it). I suspect this is the cause of your crash as it looks like you will be moving a block into a cell that is already occupied.
Second, when isCellOccupied returns true you should set canGoDown to false and break out of your for loop (or better yet, make canGoDown (==true) an additional condition of your for loop i.e. j < temporaryCopy->Length && canGoDown). As it is, your function always return true because it is never set to false and that can't be right.
Just making an assumption here, but don't all mySquares consist of 4 elements? You are initializing temporaryCopy with 4 elements but it isn't clear whether mySquares has 4 elements. If not, this could be dangerous as in your first loop you are looping on mySquares->Length and addressing temporaryCopy with that index value, which could be out of range. And then later doing the opposite. It might be better to use a constant (4) in all all loops or better yet, always use mySquares->Length (especially when creating the temporaryCopy array) to ensure that both arrays contain the same number of elements.