sfml/c++ - Using Vector for Multiple Enemy Class - c++

I'm trying game development. I tried a sample from a tutorial site like this:
int number = 10;
std::vector<std::unique_ptr<enemy> > enemies1;
for (unsigned i = 0; i < number; ++i) {
enemies1.emplace_back(std::unique_ptr<enemy>(new enemy(sf::Vector2f(100 * i + 1500, 1000), sf::Vector2f(15, 10), sf::Color(255, 255, 255, 0))));
}
//update
for (unsigned i = 0; i < number; ++i) {
enemies1[i]->update();
}
//render
for (unsigned i = 0; i < number; ++i) {
window.draw(enemies1[i]->enemysprite);
}
I'm using vector for creating multi enemies from my enemy class. Everything is ok I can draw 10 enemies on screen and they are moving and such. But if I try to delete an enemy like this:
if (!enemies1.empty())
{
for (unsigned i = 0; i < number; ++i) {
if (enemies1[i]->rect.getGlobalBounds().intersects(sprite.getGlobalBounds()))
enemies1.erase(enemies1.begin() + 1);
}
}
when the program starts a collision occurs everything is stopping. My question is what's the best way to create multiple enemies because I need to spawn and delete them in my game and I'm still learning.
I don't know whether my implementation correct or not. I wonder what should I do? In this code, I can't erase enemies from this vector.
I appreciate every piece of advice.

This loop:
for (unsigned i = 0; i < number; ++i) {
if (enemies1[i]->rect.getGlobalBounds().intersects(sprite.getGlobalBounds()))
enemies1.erase(enemies1.begin() + 1);
}
is flawed, because when you call erase, all the subsequent array entries move down 1, so next time round the loop you access the wrong element. Also, you run off the end of the array.
Instead, you can use the erase / remove idiom:
enemies.erase (std::remove_if (enemies.begin(), enemies.end(),
[sprite] (const std::unique_ptr <enemy> &e)
{ return e->rect.getGlobalBounds().intersects(sprite.getGlobalBounds()); }, enemies.end());

My enemy class is this like on shared picture

Related

Cout Vector in Random Coordinates n times

I am trying to cout a vector 12 times in a loop at random coordinates. I have created my vector here:
vector<Item> sV(MAXPILLS, pill);
Currently it only prints one of the vector out. This is my code for the random coordinates of the vector and the attempt at printing 12 of them out. I'd appreaciate it if you could help!
void generatePowerPills(char gr[][SIZEX], Item pill){
for (int i = 0; i < 12; i++)
{
gr[pill.y][pill.x] = pill.symbol;
}
}
void initialiseRandomPillsCoordinates(vector<Item>& sV) {
//pass the vector to the function to give each pill random coordinates
Seed();
for (size_t i(0); i < sV.size(); ++i)
{
sV.at(i).y = Random(SIZEY - 2); //vertical coordinate in range [1..(SIZEY - 2)]
sV.at(i).x = Random(SIZEX - 2); //horizontal coordinate in range [1..(SIZEX - 2)]
}
}
I'd just comment but sadly I can only answer. Anyway, you're iterating here:
for (int i = 0; i < 12; i++)
{
gr[pill.y][pill.x] = pill.symbol;
}
But where are you using "i" inside this loop? It seems it will do the same, 12 times. Unless there's some hidden functionality somewhere inside, if so, shame on me.

Extra sprite spawning when the enemies are redrawn SFML

I am making a game in sfml and at the moment when all of the enemies die. They are set to respawn however when this is happening they are respawning with one extra sprite than before.
The code for loading in the sprites is
unsigned int orcNumber = 5;
for (int i = 0; i < orcNumber; i++)
{
SpriteVector.push_back(ogreSprite);
SpriteVector[i].setPosition(spawnPointX[i], spawnPointY[i]);
}
The code for removing the enemies if they are offscreen or shot is similar to below using erase.
for (unsigned j = 0; j < SpriteVector.size(); j++)
{
if (this->SpriteVector[j].getPosition().x < 0 - 80 )
{
//this succesfully removes the object from the vector
SpriteVector.erase(SpriteVector.begin() + j);
std::cout << "Container size..." << SpriteVector.size() << "\n";
}
}
The statement for redrawing them is:
unsigned int orcNumberRespawn = 5;
if (SpriteVector.size() <= 1)
{
for (int i = 0; i < orcNumberRespawn; i++)
{
SpriteVector.push_back(ogreSprite);
SpriteVector[i].setPosition(spawnPointX[i], spawnPointY[i]);
}
}
window.draw(SpriteVector[i]);
Can anyone identify why when the sprites need to be redrawn it draws with + 1 sprite everytime?
The issue was with a loop outside of these vector loops.

Algorithm for smoothing

I wrote this code for smoothing of a curve .
It takes 5 points next to a point and adds them and averages it .
/* Smoothing */
void smoothing(vector<Point2D> &a)
{
//How many neighbours to smooth
int NO_OF_NEIGHBOURS=10;
vector<Point2D> tmp=a;
for(int i=0;i<a.size();i++)
{
if(i+NO_OF_NEIGHBOURS+1<a.size())
{
for(int j=1;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x+=a.at(i+j).x;
a.at(i).y+=a.at(i+j).y;
}
a.at(i).x/=NO_OF_NEIGHBOURS;
a.at(i).y/=NO_OF_NEIGHBOURS;
}
else
{
for(int j=1;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x+=tmp.at(i-j).x;
a.at(i).y+=tmp.at(i-j).y;
}
a.at(i).x/=NO_OF_NEIGHBOURS;
a.at(i).y/=NO_OF_NEIGHBOURS;
}
}
}
But i get very high values for each point, instead of the similar values to the previous point . The shape is maximized a lot , what is going wrong in this algorithm ?
What it looks like you have here is a bass-ackwards implementation of a finite impulse response (FIR) filter that implements a boxcar window function. Thinking about the problem in terms of DSP, you need to filter your incoming vector with NO_OF_NEIGHBOURS equal FIR coefficients that each have a value of 1/NO_OF_NEIGHBOURS. It is normally best to use an established algorithm rather than reinvent the wheel.
Here is a pretty scruffy implementation that I hammered out quickly that filters doubles. You can easily modify this to filter your data type. The demo shows filtering of a few cycles of a rising saw function (0,.25,.5,1) just for demonstration purposes. It compiles, so you can play with it.
#include <iostream>
#include <vector>
using namespace std;
class boxFIR
{
int numCoeffs; //MUST be > 0
vector<double> b; //Filter coefficients
vector<double> m; //Filter memories
public:
boxFIR(int _numCoeffs) :
numCoeffs(_numCoeffs)
{
if (numCoeffs<1)
numCoeffs = 1; //Must be > 0 or bad stuff happens
double val = 1./numCoeffs;
for (int ii=0; ii<numCoeffs; ++ii) {
b.push_back(val);
m.push_back(0.);
}
}
void filter(vector<double> &a)
{
double output;
for (int nn=0; nn<a.size(); ++nn)
{
//Apply smoothing filter to signal
output = 0;
m[0] = a[nn];
for (int ii=0; ii<numCoeffs; ++ii) {
output+=b[ii]*m[ii];
}
//Reshuffle memories
for (int ii = numCoeffs-1; ii!=0; --ii) {
m[ii] = m[ii-1];
}
a[nn] = output;
}
}
};
int main(int argc, const char * argv[])
{
boxFIR box(1); //If this is 1, then no filtering happens, use bigger ints for more smoothing
//Make a rising saw function for demo
vector<double> a;
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
a.push_back(0.); a.push_back(0.25); a.push_back(0.5); a.push_back(0.75); a.push_back(1.);
box.filter(a);
for (int nn=0; nn<a.size(); ++nn)
{
cout << a[nn] << endl;
}
}
Up the number of filter coefficients using this line to see a progressively more smoothed output. With just 1 filter coefficient, there is no smoothing.
boxFIR box(1);
The code is flexible enough that you can even change the window shape if you like. Do this by modifying the coefficients defined in the constructor.
Note: This will give a slightly different output to your implementation as this is a causal filter (only depends on current sample and previous samples). Your implementation is not causal as it looks ahead in time at future samples to make the average, and that is why you need the conditional statements for the situation where you are near the end of your vector. If you want output like what you are attempting to do with your filter using this algorithm, run the your vector through this algorithm in reverse (This works fine so long as the window function is symmetrical). That way you can get similar output without the nasty conditional part of algorithm.
in following block:
for(int j=0;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x=a.at(i).x+a.at(i+j).x;
a.at(i).y=a.at(i).y+a.at(i+j).y;
}
for each neighbour you add a.at(i)'s x and y respectively to neighbour values.
i understand correctly, it should be something like this.
for(int j=0;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x += a.at(i+j+1).x
a.at(i).y += a.at(i+j+1).y
}
Filtering is good for 'memory' smoothing. This is the reverse pass for the learnvst's answer, to prevent phase distortion:
for (int i = a.size(); i > 0; --i)
{
// Apply smoothing filter to signal
output = 0;
m[m.size() - 1] = a[i - 1];
for (int j = numCoeffs; j > 0; --j)
output += b[j - 1] * m[j - 1];
// Reshuffle memories
for (int j = 0; j != numCoeffs; ++j)
m[j] = m[j + 1];
a[i - 1] = output;
}
More about zero-phase distortion FIR filter in MATLAB: http://www.mathworks.com/help/signal/ref/filtfilt.html
The current-value of the point is used twice: once because you use += and once if y==0. So you are building the sum of eg 6 points but only dividing by 5. This problem is in both the IF and ELSE case. Also: you should check that the vector is long enough otherwise your ELSE-case will read at negative indices.
Following is not a problem in itself but just a thought: Have you considered to use an algorithm that only touches every point twice?: You can store a temporary x-y-value (initialized to be identical to the first point), then as you visit each point you just add the new point in and subtract the very-oldest point if it is further than your NEIGHBOURS back. You keep this "running sum" updated for every point and store this value divided by the NEIGHBOURS-number into the new point.
You make addition with point itself when you need to take neighbor points - just offset index by 1:
for(int j=0;j<NO_OF_NEIGHBOURS;j++)
{
a.at(i).x += a.at(i+j+1).x
a.at(i).y += a.at(i+j+1).y
}
This works fine for me:
for (i = 0; i < lenInput; i++)
{
float x = 0;
for (int j = -neighbours; j <= neighbours; j++)
{
x += input[(i + j <= 0) || (i + j >= lenInput) ? i : i + j];
}
output[i] = x / (neighbours * 2 + 1);
}

EXC_BAD_ACCESS on raffle-sorting program

I have a "raffle" C++ program that I use to "draw out of a hat". I receive an EXC_BAD_ACCESS signal when I try to use it, though. Here is the function:
vector<int> speedRaffle(vector<Player>players,int pNum){
vector<int> spdtics,order;
int ticnum,randy;
vector<int>::iterator iter = spdtics.begin();
for (int k=0;k<pNum;k++){
for (int i=0; i<pNum; i++) {
for (int j=0; j<pow(players[i].speed,2); j++){
for (int io=0; io<order.size(); io++) {
if(order[io]!=i){
spdtics.push_back(i);
ticnum++;
}
}
}
}
randy=random() % ticnum;
for(int i=0;i<randy;i++){
iter++;
}
order[k]=*iter; //Thread 1: Program received signal: "EXC_BAD_ACCESS".
iter=spdtics.begin();
}
return order;
}
This function should take all of the players' speeds and square them. Then, it puts that many (the squares of speeds) "raffle tickets" into spdtics. It then randomly draws one "ticket" from spdtics, and puts the number of the player who owned the ticket into order. Then, it repeats again until all players have been drawn, not drawing the same player twice. It returns the order in which the players won.
The class Player contains an int speed. I call this function like this:
order=speedRaffle(players,pNum);
where players is vector and pNum is int. What am I doing wrong?
1. You are trying to access element at index k in empty vector order
It crashes because vector order is empty when you call order[k] = *iter;, you should use push_back function instead: order.push_back(*iter);.
2. You use loop for "moving" iterator instead of simple advance call
advance(iter, randy - 1); has same effect as this loop: for(int i=0;i<randy;i++){ iter++; }.
3. You call pow in every single iteration
for (int j=0; j<pow(players[i].speed,2); j++)
Note, that this would be much faster:
int maxspeed = pow(players[i].speed,2);
for (int j = 0; j < maxspeed; j++)
4. Elements in vector can be accessed directly by using index
You don't need any iterator at all in this case.
5. Passing vector by value instead of passing it by reference
vector<int> speedRaffle(vector<Player>players,int pNum)
Note, that copy of vector players is created every time you call this function. You don't want to do that. You also don't want to change this vector inside of function, so declaring this argument as const would be much better:
vector<int> speedRaffle(const vector<Player>& players, int pNum)
6. Your code does not do what you need it to do
"It should take all of the players' speeds and square them. Then, it puts that many (the squares of speeds) "raffle tickets" into spdtics. It then randomly draws one "ticket" from spdtics, and puts the number of the player who owned the ticket into order. Then, it repeats again until all players have been drawn, not drawing the same player twice. It returns the order in which the players won."
According to this, your function should look like this:
vector<int> speedRaffle(vector<Player>& players)
{
// create vector of tickets:
set<int> ticketOwners;
vector<int> spdtics;
for (int i = 0; i < players.size(); i++)
{
ticketOwners.insert(i);
int maxspeed = pow(players[i].speed,2);
for (int j = 0; j < maxspeed; j++)
{
spdtics.push_back(i);
}
}
// draw ticket for every player:
vector<int> order;
while (!ticketOwners.empty())
{
set<int>::const_iterator to;
int randy;
do
{
randy = random() % spdtics.size();
to = ticketOwners.find(spdtics[randy]);
}
while (to == ticketOwners.end());
spdtics.erase(spdtics.begin() + randy);
order.push_back(*to);
ticketOwners.erase(to);
}
return order;
}
Also note that you don't need pNum argument if it's equal to players.size().
Hope this will help you.

Multiple animation at a time

I'm making a Asteroids game but I can not get to play more than one explosion at a time. Just get to do one at a time ...
This is my code I call in the main loop:
for(i = 0; i < MAX_SHOTS; i++) {
for(j = 0; j < MAX_ASTEROIDS; j++) {
if(shot[i].CheckCollision(asteroide[j])) {
shot[i].SetPos(-100, 0);
explosionSnd.Play();
numAst = j;
explosion[numExp++].Enable(true);
if(numExp == MAX_EXPLOSIONS-1) {
numExp = 1;
}
}
}
}
for(i = 1; i < MAX_EXPLOSIONS; i++) {
if(explosion[i].Enable()) {
explosion[i].SetPos(asteroide[numAst].GetX(), asteroide[numAst].GetY());
explosion[i].Draw();
if(explosion[i].GetFrame() == 5) {
explosion[i].Enable(false);
}
}
}
If I shot to an asteroid and after I shot to another, the animation is cut and goes to the new asteroid.
Any help?
Thank you.
Inside your second loop, you're moving each explosion to the location of the asteroid asteroide[numAst] - you're playing all the explosions, just all at the same place!
You should only position the explosion once after you Enable(true) it, when it's created in the first loop, not each time you draw it.
Hope that helps.