Space Invaders – 2D Vector Movement Algorithm - c++

I'm building Space Invaders in C++ (using the MBed platform) for a microcontroller. I've used a 2D Vector of object pointers to organise the invaders.
The movement algorithm is below, and runs in the main while loop for the game. Basically, I get the highest/lowest x and y values of invaders in the vector, and use those to set bounds based on screensize (the HEIGHT variable);
I also get the first invader's position, velocity, and width, which I apply changes to based on the bounds above.
Then I iterate through the whole vector again and apply all those changes. It sort of works – the invaders move – but the bounds don't seem to take effect, and so they fly off screen. I feel like I'm missing something really dumb, thanks in advance!
void Army::move_army() {
int maxy = HEIGHT - 20;
int Ymost = 0; // BOTTOM
int Yleast = 100; // TOP
int Xmost = 0; // LEFT
int Xleast = 100; // RIGHT
int first_row = _rows;
int first_column = _columns;
int firstWidth = 0;
Vector2D firstPos;
Vector2D firstVel;
for (int i = 0; i < _rows; i++) {
for (int n = 0; n < _columns; n++) {
bool state = invaders[i][n]->get_death();
if (!state) {
if (i < first_row && n < first_column) {
firstPos = invaders[i][n]->get_pos();
firstVel = invaders[i][n]->get_velocity();
firstWidth = invaders[i][n]->get_width();
}
Vector2D pos = invaders[i][n]->get_pos();
if (pos.y > Ymost) {Ymost = pos.y;} // BOTTOM
else if (pos.y < Yleast) {Yleast = pos.y;} // TOP
else if (pos.x > Xmost) {Xmost = pos.x;} // LEFT
else if (pos.x < Xleast) {Xleast = pos.x;} // RIGHT
}
}
}
firstVel.y = 0;
if (Xmost >= (WIDTH - 8) || Xleast <= 2) {
firstVel.x = -firstVel.x;
firstPos.y += _inc;
// reverse x velocity
// increment y position
}
else if (Ymost > maxy) {
_inc = -_inc;
// reverse increment
}
else if (Yleast < 2) {
_inc = -_inc;
// reverse increment
}
for (int i = 0; i < _rows; i++) {
int setx = firstPos.x;
if (i > 0) {firstPos.y += 9;}
for (int n = 0; n < _columns; n++) {
invaders[i][n]->set_velocity(firstVel);
invaders[i][n]->set_pos(setx,firstPos.y);
setx += firstWidth + 2;
}
}

It looks like you have your assignment cases reversed. Assignment always goes: right <- left, so in the first case you're changing the YMost value, not pos.y. It looks like if you swap those four assignments in your bounds checking it should work. Good luck!

Related

Fill Matrix in Spiral Form from center

I recently finished making an algorithm for a project I'm working on.
Briefly, a part of my project needs to fill a matrix, the requirements of how to do it are these:
- Fill the matrix in form of spiral, from the center.
- The size of the matrix must be dynamic, so the spiral can be large or small.
- Every two times a cell of the matrix is filled, //DO STUFF must be executed.
In the end, the code that I made works, it was my best effort and I am not able to optimize it more, it bothers me a bit having had to use so many ifs, and I was wondering if someone could take a look at my code to see if it is possible to optimize it further or some constructive comment (it works well, but it would be great if it was faster, since this algorithm will be executed several times in my project). Also so that other people can use it!
#include <stdio.h>
typedef unsigned short u16_t;
const u16_t size = 7; //<-- CHANGE HERE!!! just odd numbers and bigger than 3
const u16_t maxTimes = 2;
u16_t array_cont[size][size] = { 0 };
u16_t counter = 3, curr = 0;
u16_t endColumn = (size - 1) / 2, endRow = endColumn;
u16_t startColumn = endColumn + 1, startRow = endColumn + 1;
u16_t posLoop = 2, buffer = startColumn, i = 0;
void fillArray() {
if (curr < maxTimes) {
if (posLoop == 0) { //Top
for (i = buffer; i <= startColumn && curr < maxTimes; i++, curr++)
array_cont[endRow][i] = counter++;
if (curr == maxTimes) {
if (i <= startColumn) {
buffer = i;
} else {
buffer = endRow;
startColumn++;
posLoop++;
}
} else {
buffer = endRow;
startColumn++;
posLoop++;
fillArray();
}
} else if (posLoop == 1) { //Right
for (i = buffer; i <= startRow && curr < maxTimes; i++, curr++)
array_cont[i][startColumn] = counter++;
if (curr == maxTimes) {
if (i <= startRow) {
buffer = i;
} else {
buffer = startColumn;
startRow++;
posLoop++;
}
} else {
buffer = startColumn;
startRow++;
posLoop++;
fillArray();
}
} else if (posLoop == 2) { //Bottom
for (i = buffer; i >= endColumn && curr < maxTimes; i--, curr++)
array_cont[startRow][i] = counter++;
if (curr == maxTimes) {
if (i >= endColumn) {
buffer = i;
} else {
buffer = startRow;
endColumn--;
posLoop++;
}
} else {
buffer = startRow;
endColumn--;
posLoop++;
fillArray();
}
} else if (posLoop == 3) { //Left
for (i = buffer; i >= endRow && curr < maxTimes; i--, curr++)
array_cont[i][endColumn] = counter++;
if (curr == maxTimes) {
if (i >= endRow) {
buffer = i;
} else {
buffer = endColumn;
endRow--;
posLoop = 0;
}
} else {
buffer = endColumn;
endRow--;
posLoop = 0;
fillArray();
}
}
}
}
int main(void) {
array_cont[endColumn][endColumn] = 1;
array_cont[endColumn][endColumn + 1] = 2;
//DO STUFF
u16_t max = ((size * size) - 1) / maxTimes;
for (u16_t j = 0; j < max; j++) {
fillArray();
curr = 0;
//DO STUFF
}
//Demostration
for (u16_t x = 0; x < size; x++) {
for (u16_t y = 0; y < size; y++)
printf("%-4d ", array_cont[x][y]);
printf("\n");
}
return 0;
}
Notice that the numbers along the diagonal (1, 9, 25, 49) are the squares of the odd numbers. That's an important clue, since it suggests that the 1 in the center of the matrix should be treated as the end of a spiral.
From the end of each spiral, the x,y coordinates should be adjusted up and to the right by 1. Then the next layer of the spiral can be constructed by moving down, left, up, and right by the same amount.
For example, starting from the position of the 1, move up and to the right (to the position of the 9), and then form a loop with the following procedure:
move down, and place the 2
move down, and place the 3
move left, and place the 4
move left, and place the 5
etc.
Thus the code looks something like this:
int size = 7;
int matrix[size][size];
int dy[] = { 1, 0, -1, 0 };
int dx[] = { 0, -1, 0, 1 };
int directionCount = 4;
int ringCount = (size - 1) / 2;
int y = ringCount;
int x = ringCount;
int repeatCount = 0;
int value = 1;
matrix[y][x] = value++;
for (int ring = 0; ring < ringCount; ring++)
{
y--;
x++;
repeatCount += 2;
for (int direction = 0; direction < directionCount; direction++)
for (int repeat = 0; repeat < repeatCount; repeat++)
{
y += dy[direction];
x += dx[direction];
matrix[y][x] = value++;
}
}
I saw already many approaches for doing a spiral. All a basically drawing it, by following a path.
BUT, you can also come up with an analytical calculation formula for a spiral.
So, no recursion or iterative solution by following a path or such. We can directly calculate the indices in the matrix, if we have the running number.
I will start with the spiral in mathematical positive direction (counter clockwise) in a cartesian coordinate system. We will concentrate on X and Y coordinates.
I made a short Excel and derived some formulas from that. Here is a short picture:
From the requirements we know that the matrix will be quadratic. That makes things easier. A little bit trickier is, to get the matrix data symmetrical. But with some simple formulas, derived from the prictures, this is not really a problem.
And then we can calculate x and y coordinates with some simple statements. See the below example program with long variable names for better understanding. The code is made using some step by step approach to illustrate the implementation. Of course it can be made more compact easily. Anyway. Let's have a look.
#include <iostream>
#include <cmath>
#include <iomanip>
int main() {
// Show some example values
for (long step{}; step < 81; ++step) {
// Calculate result
const long roundedSquareRoot = std::lround(std::sqrt(step));
const long roundedSquare = roundedSquareRoot * roundedSquareRoot;
const long distance = std::abs(roundedSquare - step) - roundedSquareRoot;
const long rsrIsOdd = (roundedSquareRoot % 2);
const long x = (distance + roundedSquare - step - rsrIsOdd) / (rsrIsOdd ? -2 : 2);
const long y = (-distance + roundedSquare - step - rsrIsOdd) / (rsrIsOdd ? -2 : 2);
// Show ouput
std::cout << "Step:" << std::setw(4) << step << std::setw(3) << x << ' ' << std::setw(3) << y << '\n';
}
}
So, you see that we really have an analytical solution. Given any number we can calculate the x and y coordinate using a formula. Cool.
Getting indices in a matrix is just adding some offset.
With that gained know how, we can now easily calculate the complete matrix. And, since there is no runtime activity needed at all, we can let the compiler do the work. We will simply use constexpr functions for everything.
Then the compiler will create this matrix at compile time. At runtime, nothing will happen.
Please see a very compact solution:
#include <iostream>
#include <iomanip>
#include <array>
constexpr size_t MatrixSize = 15u;
using MyType = long;
static_assert(MatrixSize > 0 && MatrixSize%2, "Matrix size must be odd and > 0");
constexpr MyType MatrixHalf = MatrixSize / 2;
using Matrix = std::array<std::array<MyType, MatrixSize>, MatrixSize >;
// Some constexpr simple mathematical functions ------------------------------------------------------------------------------
// No need for <cmath>
constexpr MyType myAbs(MyType v) { return v < 0 ? -v : v; }
constexpr double mySqrtRecursive(double x, double c, double p) {return c == p? c: mySqrtRecursive(x, 0.5 * (c + x / c), c); }
constexpr MyType mySqrt(MyType x) {return (MyType)(mySqrtRecursive((double)x,(double)x,0.0)+0.5); }
// Main constexpr function will fill the matrix with a spiral pattern during compile time -------------------------------------
constexpr Matrix fillMatrix() {
Matrix matrix{};
for (int i{}; i < (MatrixSize * MatrixSize); ++i) {
const MyType rsr{ mySqrt(i) }, rs{ rsr * rsr }, d{ myAbs(rs - i) - rsr }, o{ rsr % 2 };
const size_t col{ (size_t)(MatrixHalf +((d + rs - i - o) / (o ? -2 : 2)))};
const size_t row{ (size_t)(MatrixHalf -((-d + rs - i - o) / (o ? -2 : 2)))};
matrix[row][col] = i;
}
return matrix;
}
// This is a compile time constant!
constexpr Matrix matrix = fillMatrix();
// All the above has been done during compile time! -----------------------------------------
int main() {
// Nothing to do. All has beend done at compile time already!
// The matrix is already filled with a spiral pattern
// Just output
for (const auto& row : matrix) {
for (const auto& col : row) std::cout << std::setw(5) << col << ' '; std::cout << '\n';
}
}
Different coordinate systems or other spiral direction can be adapted easily.
Happy coding.

How do I do collision between two RectangleShapes in SFML 2.5.0? (C++)

So I'm relatively new to coding in SFML, so apologies if I made some newbie mistake. My project is Space Invaders in SFML. When I was working on the shooting, a problem arose. How do I do collision? I was familiar with shape.getGlobalBounds().intersect(), and it worked on previous projects. It didn't work. So I tried simplifying it. I used RectangleShape for both the enemy shape, and the bullet shape.
Here is the actual for loop, for the collision:
for (int y = 0; y <= 2; ++y) {
for (int x = 0; x <= 6; ++x) {
if (shootShape.getPosition().x < e.aliensX[x] && shootShape.getPosition().x > e.aliensX[x] + 15 ||
shootShape.getPosition().y < e.aliensY[y] && shootShape.getPosition().y > e.aliensY[y] + 15) {
e.aliensX[x] = -10;
e.aliensY[y] = -10;
shooting = false;
reload = false;
}
}
}
This is the shooting function:
void Player::shoot() {
if (reload) {
shootX = posX + 5;
shootY = posY - 50;
shootShape.setPosition(shootX, shootY);
shooting = true;
reload = false;
}
if (shooting) {
shootY -= 150 * 2 * deltaTime;
shootShape.setPosition(shootX, shootY);
}
}
And this is how I draw the enemies (I don't know how to create multiple enemies):
void Enemy::drawAliens() {
for (int j = 0; j <= arraySizeY; ++j) {
for (int i = 0; i <= arraySizeX; ++i) {
actualShape.setPosition(aliensX[i], aliensY[j]);
if (aliensY[i] <= p.shootY && aliensY[i] >= p.shootY) {
aliensX[i] = -10;
aliensY[i] = -10;
}
else {
win.draw(actualShape);
}
}
}
}
Explanation behind some of the variables:
aliensX is an array which contains the different x positions for the enemies.
aliensY is an array which contains the different y positions for the enemies.
shooting is a bool variable, which is true when the bullet is travelling.
reload is whether you can shoot.
EDIT:
Intersect will not work because of the way I created my enemies. They are all in ONE shape. I need it to work with specific coordinates, because I don't know a better way to create multiple enemies all at once. If there is a way to do that, advice would be appreciated!
It's rather easy, if you rely on SFML's templated sf::Rect class. Just retrieve the rectangle for both objects as global boundaries and try to intersect them:
const bool collides = firstDrawable.getGlobalBounds().intersect(secondDrawable.getGlobalBounds());`
Same can be done with a small temporary rectangle you can fill with dynamic values not directly associated with a sf::Drawable:
const sf::FloatRect bullet(x - width / 2, y - height / 2, width, height);
const bool collides = firstDrawable.getGlobalBounds().intersect(bullet);`
Your if statement is never true. You have
shootShape.getPosition().x < e.aliensX[x] && shootShape.getPosition().x > e.aliensX[x] + 15.
Your shootShape.getPosition().x can never be smaller than e.aliensX[x] and bigger than e.aliensX[x] + 15 at the same time. That's impossible. The same for y position. Change your if statement to
for (int y = 0; y <= 2; ++y) {
for (int x = 0; x <= 6; ++x) {
if (shootShape.getPosition().x > e.aliensX[x] && shootShape.getPosition().x < e.aliensX[x] + 15 ||
shootShape.getPosition().y > e.aliensY[y] && shootShape.getPosition().y < e.aliensY[y] + 15) {
e.aliensX[x] = -10;
e.aliensY[y] = -10;
shooting = false;
reload = false;
}
}
}

Getting array boundary exception that is so weird

I'm trying to implement a space invader game, now I'm trying to check if bullets hit an invader, then I remove it.
I'm ONLY get an exception when I try to shoot the last invader in the row.
For e.g If I have just one row and 8 invaders, the last one, when the bullet hits it, I got a strange boundary out of exception.
The initialization is like that, in pseudo code:
struct Invaders_t
{
vector<Invader*> *invaders;
}
for (int j = 0; j < 1; j++)
{
x_spacing = 15;
for (int i = 0; i < 8; i++)
{
Invader *invader = new Invader();
g_Invaders.invaders->push_back(invader);
}
for each frame:
for (int x = 0; x < 1; x++)
{
for (int y = 0; y < 8; y++)
{
Invader *pInvader = (*(g_Invaders.invaders))[x*8 + y];
if (pInvader != nullptr && pInvader->AlienAtals->GetImage() != nullptr)
{
for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;
if (CheckCollision(left, top, pInvader->AlienSprite->m_X, pInvader->AlienSprite->GetImage()->GetWidth()/4
, pInvader->AlienSprite->m_Y, pInvader->AlienSprite->GetImage()->GetHeight()/4))
{
std::vector<Invader*>::iterator iter = g_Invaders.invaders->begin();
std::advance(iter, (x *8) + y);
Invader *invader = *iter;
g_Invaders.invaders->erase(iter);
delete invader;
}
}
}
}
}
When you delete an invader (g_Invaders.invaders->erase(iter);), the size of the vector decreases by 1 (all vector's elements after iter shift by 1 position to the beginning, filling the vacant space). Your code doesn't expect this and continues iterating over the vector as if still had the full set of elements.

Bullets only killing first enemy

I'm making a 2D game in c++ and am trying to shoot enemies. When a bullet collides with the first enemy rendered then the enemy is killed fine, and it is removed from the enemys vector. However after the first enemy has been killed, other enemies no longer die.
This is where the check is carried out in the update function.
size = enemys.size();
for (int i = 0; i<size; i++)
{
double x = enemys[i].getEnemyX();
double y = enemys[i].getEnemyY();
bool isShot = enemyShot(x,y);
if(isShot == true){
enemys.erase(enemys.begin()+i);
size = size - 1;
}
}
This is the enemyShot function.
bool GameActivity::enemyShot(double enemyX, double enemyY)
{
int size = bullets.size();
for (int i = 0; i<size; i++)
{
double x = bullets[i].getX();
double y = bullets[i].getY();
if (x >= enemyX-5.0 && x <= enemyX+5.0 && y >= enemyY-5.0 && y <= enemyY + 5.0){
return true;
}
else{
return false;
}
}
}
The problem is that your vector of enemies is updated after each erasure - thus, the current index is no longer correct.
A better way to iterate the enemys vector is to start from the end of the enemys vector. That way, when you erase an element, the index is still correct:
for (size_t i = enemys.end (); i> 0; --i) {
double x = enemys[i].getEnemyX();
double y = enemys[i].getEnemyY();
bool isShot = enemyShot(x,y);
if(isShot == true){
enemys.erase(enemys.begin()+i);
}
}

C++ time spent allocating vectors

I am trying to speed up a piece of code that is ran a total of 150,000,000 times.
I have analysed it using "Very Sleepy", which has indicated that the code is spending the most time in these 3 areas, shown in the image:
The code is as follows:
double nonLocalAtPixel(int ymax, int xmax, int y, int x , vector<nodeStructure> &nodeMST, int squareDimension, Mat &inputImage) {
vector<double> nodeWeights(8,0);
vector<double> nodeIntensities(8,0);
bool allZeroWeights = true;
int numberEitherside = (squareDimension - 1) / 2;
int index = 0;
for (int j = y - numberEitherside; j < y + numberEitherside + 1; j++) {
for (int i = x - numberEitherside; i < x + numberEitherside + 1; i++) {
// out of range or the centre pixel
if (j<0 || i<0 || j>ymax || i>xmax || (j == y && i == x)) {
index++;
continue;
}
else {
int centreNodeIndex = y*(xmax+1) + x;
int thisNodeIndex = j*(xmax+1) + i;
// add to intensity list
Scalar pixelIntensityScalar = inputImage.at<uchar>(j, i);
nodeIntensities[index] = ((double)*pixelIntensityScalar.val);
// find weight from p to q
float weight = findWeight(nodeMST, thisNodeIndex, centreNodeIndex);
if (weight!=0 && allZeroWeights) {
allZeroWeights = false;
}
nodeWeights[index] = (weight);
index++;
}
}
}
// find min b
int minb = -1;
int bCost = -1;
if (allZeroWeights) {
return 0;
}
else {
// iteratate all b values
for (int i = 0; i < nodeWeights.size(); i++) {
if (nodeWeights[i]==0) {
continue;
}
double thisbCost = nonLocalWithb(nodeIntensities[i], nodeIntensities, nodeWeights);
if (bCost<0 || thisbCost<bCost) {
bCost = thisbCost;
minb = nodeIntensities[i];
}
}
}
return minb;
}
Firstly, I assume the spent time indicated by Very Sleepy means that the majority of time is spent allocating the vector and deleting the vector?
Secondly, are there any suggestions to speed this code up?
Thanks
use std::array
reuse the vectors by passing it as an argument of the function or a global variable if possible (not aware of the structure of the code so I need more infos)
allocate one 16 vector size instead of two vectors of size 8. Will make your memory less fragmented
use parallelism if findWeight is thread safe (you need to provide more details on that too)