I have been working on a project that will, in short, generate a 2D matrix of numbers, with "empty" spaces are represented by 0's. Each number is connected by a list of nodes. The nodes contain the number value, the number's X and Y position, and a list of all spaces adjacent to it (its "neighbors"), with the exception of spaces diagonally adjacent to the point, due to the algorithm only allowing movements of up, down, left, and right. The issue that I am having is that, as the title would suggest, I am experiencing some stack overflow issues. I will post my code below, if anyone could help, I would be most appreciative.
CoordList* Puzzle::GeneratePath(CoordList* Path, int GoalX, int GoalY)
{
int CurrX;
int CurrY;
CurrX = Path->NeighborX;
CurrY = Path->NeighborY;
if(CurrX == GoalX && CurrY == GoalY)
{
return(Path);
}
else
{
int NewX;
int NewY;
double NewDistance;
int OldX;
int OldY;
double OldDistance;
CoordList* PointNeighbors = NULL;
CoordList* BestChoice = NULL;
for(int i = 0; i < NumDirections; i++)
{
CoordList* NewNeighbor = new CoordList;
NewX = CurrX + DirectsX[i];
NewY = CurrY + DirectsY[i];
if(IsPossible(NewX, NewY))
{
NewNeighbor->NeighborX = NewX;
NewNeighbor->NeighborY = NewY;
if(PointNeighbors == NULL)
{
NewNeighbor->next = NULL;
PointNeighbors = NewNeighbor;
}
else
{
NewNeighbor->next = PointNeighbors;
PointNeighbors = NewNeighbor;
}
}
//delete NewNeighbor;
}
while(PointNeighbors != NULL)
{
if(BestChoice == NULL)
{
CoordList* AChoice = new CoordList;
AChoice->next = NULL;
NewX = PointNeighbors->NeighborX;
NewY = PointNeighbors->NeighborY;
AChoice->NeighborX = NewX;
AChoice->NeighborY = NewY;
BestChoice = AChoice;
PointNeighbors = PointNeighbors->next;
//delete AChoice;
}
else
{
NewX = PointNeighbors->NeighborX;
NewY = PointNeighbors->NeighborY;
NewDistance = DetermineDistance(NewX, NewY, GoalX, GoalY);
OldX = BestChoice->NeighborX;
OldY = BestChoice->NeighborY;
OldDistance = DetermineDistance(OldX, OldY, GoalX, GoalY);
if(NewDistance < OldDistance)
{
BestChoice->NeighborX = NewX;
BestChoice->NeighborY = NewY;
}
PointNeighbors = PointNeighbors->next;
}
}
BestChoice->next = Path;
Path = BestChoice;
return(GeneratePath(Path, GoalX, GoalY));
}
}
I was asked to provide my determine distance function. This is just a simple implementation of the traditional Point Distance formula. Provided below.
double Puzzle::DetermineDistance(int OneX, int OneY, int TwoX, int TwoY)
{
int DifX;
int DifY;
double PointSum;
DifX = (TwoX - OneX);
DifY = (TwoY - OneY);
DifX = (DifX * DifX);
DifY = (DifY * DifY);
PointSum = (DifX + DifY);
return (sqrt(PointSum));
}
The following is the IsPossible function, which determines if an X and Y value lies within the possible grid space.
bool Puzzle::IsPossible(int x, int y)
{
if(x + 1 > Size - 1 || x - 1 < 0
|| y + 1 > Size - 1 || y - 1 < 0)
{
return false;
}
return true;
}
You might have a infinite recursion loop that causes the stackoverflow, as you make new local variables every recursion, especially with your observered oscillation behaviour. I assume you dont have that problem with small matrices. Its just a shot in the dark :-)
The oscillation problem indicates that you dont check whether you have already been on one place already?
Anyways, maybe you want to reconsider using another pathfinding algorithm. I would suggest a agent based solution. I used to use the following solution to solve a maze of similar structure: I started an agent with a "PositionsList" of spots where it have been, so in the beginning only with the starting point. Then it copied itself to every reachable position not being in his own PositionList, adding the new position to that list and destroying itself then. Repeat that pattern with all new agents until the first agent reaches the goal. That way you are guaranteed to find the optimal path. But it might get pretty memory heavy for big matrices, especially when there are a lot different ways to get to the goal and a lot of possible directions per position! But there are plenty of other very good pathfinding algorithms out there. Maybe one of them suits you well :-)
Good Luck!
Related
I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner. I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free). I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.
My problem is a sort of 'left-bias' in how the particles move; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way. So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left. This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it. I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing. Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing? Any help would be very much appreciated.
EDIT:
Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that. This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally. I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.
New snippets:
Simulation.cpp
void Simulation::update()
{
copyStates(m_cells, m_oldCells); // so now oldcells is the last new state
for(int y = m_height - 1; y>= 0; y--)
for(int x = 0; x < m_width; x++)
{
Cell* c = getOldCell(x, y); // check in the old state for possible updates
switch(c->m_type)
{
case EMPTY:
break;
case SAND:
if(c->m_visited == false) update_sand(x, y);
break;
case WATER:
if(c->m_visited == false) update_water(x, y);
break;
default:
break;
}
}
}
void Simulation::update_water(int x, int y)
{
bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;
// choose random dir if both are possible
if(d_left && d_right)
{
int r = rand() % 2;
if(r) d_right = false;
else d_left = false;
}
if(left && right)
{
int r = rand() % 2;
if(r) right = false;
else left = false;
}
if(down)
{
getCell(x, y+1)->m_type = WATER; // we now update the new state
getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
} else if(d_left)
{
getCell(x-1, y+1)->m_type = WATER;
getOldCell(x-1, y+1)->m_visited = true;
} else if(d_right)
{
getCell(x+1, y+1)->m_type = WATER;
getOldCell(x+1, y+1)->m_visited = true;
} else if(left)
{
getCell(x-1, y)->m_type = WATER;
getOldCell(x-1, y)->m_visited = true;
} else if(right)
{
getCell(x+1, y)->m_type = WATER;
getOldCell(x+1, y)->m_visited = true;
}
if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
{
getCell(x, y)->m_type = EMPTY;
}
}
void Simulation::copyStates(Cell* from, Cell* to)
{
for(int x = 0; x < m_width; x++)
for(int y = 0; y < m_height; y++)
{
to[x + y * m_width].m_type = from[x + y * m_width].m_type;
to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
}
}
Main.cpp
sim.update();
Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
sim.getCell(x, y)->m_visited = false;
if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}
screen.render();
I've attached a gif showing the problem, hopefully this might help make it a little clearer. You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)
You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.
I am kinda stuck with my basic voxel physics right now. It's very, very choppy and I am pretty sure my maths is broken somewhere, but let's see what you have to say:
// SOMEWHERE AT CLASS LEVEL (so not being reinstantiated every frame, but persisted instead!)
glm::vec3 oldPos;
// ACTUAL IMPL
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
if (!entity->grounded) {
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) {
glm::vec3 dir = entityPosition - oldPos; // Actually no need to normalize as we check for lesser, bigger or equal to 0
std::cout << "falling dir: " << glm::to_string(dir) << std::endl;
// Calculate offset (where to put after hit)
int x = dir.x;
int y = dir.y;
int z = dir.z;
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
if (dir.y >= 0) {
y = -1;
} else if (dir.y < 0) {
y = 1;
}
if (dir.z >= 0) {
z = -1;
} else if (dir.z < 0) {
z = 1;
}
glm::vec3 newPos = oldPos + glm::vec3(x, y, z);
this->entity->setPosition(newPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
} else {
oldPos = entity->getPosition();
this->entity->setPosition(distanceToGravityCenter);
}
}
Basic idea was to determine from which direction entityt would hit the surface and then just position it one "unit" back into that direction. But obviously I am doing something wrong as that will always move entity back to the point where it came from, effectively holding it at the spawn point.
Also this could probably be much easier and I am overthinking it.
As #CompuChip already pointed out, your ifs could be further simplified.
But what is more important is one logical issue that would explain the "choppiness" you describe (Sadly you did not provide any footage, so this is my best guess)
From the code you posted:
First you check if entity is grounded. If so you continue with checking if there is a collision and lastly, if there is not, you set the position.
You have to invert that a bit.
Save old position
Check if grounded
Set the position already to the new one!
Do collision detection
Reset to old position IF you registered a collision!
So basically:
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
oldPos = entity->getPosition(); // 1.
if (!entity->grounded) { // 2.
this->fallingStar->setPosition(distanceToGravityPoint); // 3
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) { // 4, 5
this->entity->setPosition(oldPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
}
}
This should get you started :)
I want to elaborate a bit more:
If you check for collision first and then set position you create an "infinite loop" upon first collision/hit as you collide, then if there is a collision (which there is) you set back to the old position. Basically just mathematic inaccuracy will make you move, as on every check you are set back to the old position.
Consider the if-statements for one of your coordinates:
if (dir.x >= 0) {
x = -1;
}
if (dir.x < 0) {
x = 1;
}
Suppose that dir.x < 0. Then you will skip the first if, enter the second, and x will be set to 1.
If dir.x >= 0, you will enter the first if and x will be set to -1. Now x < 0 is true, so you will enter the second if as well, and x gets set to 1 again.
Probably what you want is to either set x to 1 or to -1, depending on dir.x. You should only execute the second if when the first one was not entered, so you need an else if:
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
which can be condensed, if you so please, into
x = (dir.x >= 0) ? -1 : 1;
Sorry if that title isn't very descriptive. Anyway, I am working on something dealing with randomly generating landscapes. I made lakes, but due to how they are make, they often cause straight edges / dropoffs, which aren't desirable. I am trying to smooth it (right after making the lake, if possible), by defining a max variation amount (so land heights cannot vary more than it), and have it fix land if it varies to much, and quit if it is fine.
The problem:
My attempted fix:
As you can see... it didn't work. It also occurs to me, I think it would be broken if it had to move down, although that case shouldn't actually occur, because lakes only ever sink the landscape. Anyway, here is the source of my attempt:
//smoothing land nearby
int maxVariation = 2; //similar to the max height variation when the land is generated
//going right
for (int xPos = rightBound + 1, previousHeight = 0; ; ++xPos)
{
if (previousHeight == 0)
for (; previousHeight < size.y; ++previousHeight)
if (grid[0][rightBound][previousHeight] != BlockColor::DIRT && grid[0][rightBound][previousHeight] != BlockColor::GRASS)
{
--previousHeight;
break;
}
for (int y = 0; y < size.y; ++y)
if (grid[0][xPos][y] == BlockColor::WATER)
goto done_smoothing_right;
int height;
for (height = 0; height < size.y; ++height)
if (grid[0][xPos][height] != BlockColor::DIRT && grid[0][xPos][height] != BlockColor::GRASS)
{
--height;
break;
}
int difference = std::abs(height - previousHeight);
previousHeight = height;
if (difference > maxVariation)
{
for (int j = 0; j < size.y; ++j)
{
int toMove = difference;
while (j + toMove >= size.y)
--toMove;
grid[0][xPos][j] = grid[0][xPos][j + toMove];
}
}
else
goto done_smoothing_right;
}
done_smoothing_right:
int tomakegotowork;
Note that is only the right side, but left should be about the same. How can I do this correctly?
Thanks if you can help.
EDIT:
I never did solve this problem. Instead, I made a recursive function to measure air, (from a certain height), and if a pocket of air (formed by the land) had enough, to fill with water. This has the advantaged of the land looking smooth because it is not altered.
This is written in java so you will need to convert it to c++ but it should give you the basic idea. It will only work for smoothing upwards as well and I only did the right side of the lake but it is very easy to modify it for the left side of the lake. I tried to match what I think the functionality of your code is.
Hope it helps...
void smoothLakeRight(Lake lake){
int x = lake.rightBound+1;
if(getGrassHeight(x)-lake.height>WorldConstants.MAX_LAKESIDE_VARIATION){
//if the right bank is too high start smoothing
int y =lake.height+WorldConstants.MAX_LAKESIDE_VARIATION;
while(grid[0][x][y] == BlockColor.DIRT){
fixGrass(x++, y++);
}
}
}
private int getGrassHeight(int xPos){
int y = WorldConstants.LOWEST_GRASS;
while(grid[0][xPos][y++] != BlockColor.GRASS);
return y-1;
}
private void fixGrass(int xPos, int yPos){
grid[0][xPos][yPos] = BlockColor.GRASS;
aboveAir(xPos,yPos);
belowDirt(xPos, yPos);
}
private void aboveAir(int xPos, int yPos) {
while(grid[0][xPos][++yPos]!=BlockColor.AIR){
if(grid[0][xPos][yPos]==BlockColor.TREE){
upRootTree(xPos, yPos);
}else{
grid[0][xPos][yPos]=BlockColor.AIR;
}
}
}
private void upRootTree(int xPos, int yPos) {
while(grid[0][xPos][yPos]==BlockColor.TREE){//remove stump
grid[0][xPos][yPos++]=BlockColor.AIR;
}
//remove leaves
grid[0][xPos][yPos] = BlockColor.AIR;
grid[0][xPos+1][yPos] = BlockColor.AIR;
grid[0][xPos-1][yPos] = BlockColor.AIR;
grid[0][xPos+1][yPos-1] = BlockColor.AIR;
grid[0][xPos-1][yPos-1] = BlockColor.AIR;
}
private void belowDirt(int xPos, int yPos) {
while(grid[0][xPos][--yPos]!=BlockColor.DIRT){
grid[0][xPos][yPos] = BlockColor.DIRT;
}
}
The problem to solve is finding the floating status of a floating body, given its weight and the center of gravity.
The function i use calculates the displaced volume and center of bouyance of the body given sinkage, heel and trim.
Where sinkage is a length unit and heel/trim is an angle limited to a value from -90 to 90.
The floating status is found when displaced volum is equal to weight and the center of gravity is in a vertical line with center of bouancy.
I have this implemeted as a non-linear Newton-Raphson root finding problem with 3 variables (sinkage, trim, heel) and 3 equations.
This method works, but needs good initial guesses. So I am hoping to find either a better approach for this, or a good method to find the initial values.
Below is the code for the newton and jacobian algorithm used for the Newton-Raphson iteration. The function volume takes the parameters sinkage, heel and trim. And returns volume, and the coordinates for center of bouyancy.
I also included the maxabs and GSolve2 algorithms, I belive these are taken from Numerical Recipies.
void jacobian(float x[], float weight, float vcg, float tcg, float lcg, float jac[][3], float f0[]) {
float h = 0.0001f;
float temp;
float j_volume, j_vcb, j_lcb, j_tcb;
float f1[3];
volume(x[0], x[1], x[2], j_volume, j_lcb, j_vcb, j_tcb);
f0[0] = j_volume-weight;
f0[1] = j_tcb-tcg;
f0[2] = j_lcb-lcg;
for (int i=0;i<3;i++) {
temp = x[i];
x[i] = temp + h;
volume(x[0], x[1], x[2], j_volume, j_lcb, j_vcb, j_tcb);
f1[0] = j_volume-weight;
f1[1] = j_tcb-tcg;
f1[2] = j_lcb-lcg;
x[i] = temp;
jac[0][i] = (f1[0]-f0[0])/h;
jac[1][i] = (f1[1]-f0[1])/h;
jac[2][i] = (f1[2]-f0[2])/h;
}
}
void newton(float weight, float vcg, float tcg, float lcg, float &sinkage, float &heel, float &trim) {
float x[3] = {10,1,1};
float accuracy = 0.000001f;
int ntryes = 30;
int i = 0;
float jac[3][3];
float max;
float f0[3];
float gauss_f0[3];
while (i < ntryes) {
jacobian(x, weight, vcg, tcg, lcg, jac, f0);
if (sqrt((f0[0]*f0[0]+f0[1]*f0[1]+f0[2]*f0[2])/2) < accuracy) {
break;
}
gauss_f0[0] = -f0[0];
gauss_f0[1] = -f0[1];
gauss_f0[2] = -f0[2];
GSolve2(jac, 3, gauss_f0);
x[0] = x[0]+gauss_f0[0];
x[1] = x[1]+gauss_f0[1];
x[2] = x[2]+gauss_f0[2];
// absmax(x) - Return absolute max value from an array
max = absmax(x);
if (max < 1) max = 1;
if (sqrt((gauss_f0[0]*gauss_f0[0]+gauss_f0[1]*gauss_f0[1]+gauss_f0[2]*gauss_f0[2])) < accuracy*max) {
x[0]=x2[0];
x[1]=x2[1];
x[2]=x2[2];
break;
}
i++;
}
sinkage = x[0];
heel = x[1];
trim = x[2];
}
int GSolve2(float a[][3],int n,float b[]) {
float x,sum,max,temp;
int i,j,k,p,m,pos;
int nn = n-1;
for (k=0;k<=n-1;k++)
{
/* pivot*/
max=fabs(a[k][k]);
pos=k;
for (p=k;p<n;p++){
if (max < fabs(a[p][k])){
max=fabs(a[p][k]);
pos=p;
}
}
if (ABS(a[k][pos]) < EPS) {
writeLog("Matrix is singular");
break;
}
if (pos != k) {
for(m=k;m<n;m++){
temp=a[pos][m];
a[pos][m]=a[k][m];
a[k][m]=temp;
}
}
/* convert to upper triangular form */
if ( fabs(a[k][k])>=1.e-6)
{
for (i=k+1;i<n;i++)
{
x = a[i][k]/a[k][k];
for (j=k+1;j<n;j++) a[i][j] = a[i][j] -a[k][j]*x;
b[i] = b[i] - b[k]*x;
}
}
else
{
writeLog("zero pivot found in line:%d",k);
return 0;
}
}
/* back substitution */
b[nn] = b[nn] / a[nn][nn];
for (i=n-2;i>=0;i--)
{
sum = b[i];
for (j=i+1;j<n;j++)
sum = sum - a[i][j]*b[j];
b[i] = sum/a[i][i];
}
return 0;
}
float absmax(float x[]) {
int i = 1;
int n = sizeof(x);
float max = x[0];
while (i < n) {
if (max < x[i]) {
max = x[i];
}
i++;
}
return max;
}
Have you considered some stochastic search methods to find the initial value and then fine-tuning with Newton Raphson? One possibility is evolutionary computation, you can use the Inspyred package. For a physical problem similar in many ways to the one you describe, look at this example: http://inspyred.github.com/tutorial.html#lunar-explorer
What about using a damped version of Newton's method? You could quite easily modify your implementation to make it. Think about Newton's method as finding a direction
d_k = f(x_k) / f'(x_k)
and updating the variable
x_k+1 = x_k - L_k d_k
In the usual Newton's method, L_k is always 1, but this might create overshoots or undershoots. So, let your method chose L_k. Suppose that your method usually overshoots. A possible strategy consists in taking the largest L_k in the set {1,1/2,1/4,1/8,... L_min} such that the condition
|f(x_k+1)| <= (1-L_k/2) |f(x_k)|
is satisfied (or L_min if none of the values satisfies this criteria).
With the same criteria, another possible strategy is to start with L_0=1 and if the criteria is not met, try with L_0/2 until it works (or until L_0 = L_min). Then for L_1, start with min(1, 2L_0) and do the same. Then start with L_2=min(1, 2L_1) and so on.
By the way: are you sure that your problem has a unique solution? I guess that the answer to this question depends on the shape of your object. If you have a rugby ball, there's one angle that you cannot fix. So if your shape is close to such an object, I would not be surprised that the problem is difficult to solve for that angle.
{Hopefully improved my post, please still suggest any other code you need, and once again im sorry for being so clueless, im determined to get past this problem though so i truly appreciate your time!!!}
**EDIT: thanks to Frank with his reply below, the program now starts and draws the three enemies, but just a few second later crashes, the program code below therefore is still applicable as its basically the move loop and somewhere in there something is still going wrong.
I realise this is extremely obscure and i have tried my best to explain it, but if noone can advise then the few second its up should be enough to complete the tutorial anyway and ill dissect the whole project after its finished and really try and break it down and learn as much as possible.**
Okay, so I run this loop designed to create new enemies, and then draw them onto the screen, it now works, but after a few second crashes. below is the steps the debugging goes through and the call stack at the end if what it displayed after the crash. hope you can help!
this is a video tutorial im following and im stuck, cant find the answer. checked the code over and over again. (the full code is at the bottom of the post (codeblocks,) but i have tried to include as much info as possible in this post)
the function is:
level->addEnemies(3);
which looks like in the main game.cpp:
bool Game::run(void)
{
level = new Level(&drawArea, 30, 20);
drawArea.createBackgroundTile(TILE_EMPTY, ' ');
drawArea.createBackgroundTile(TILE_WALL, 219);
drawArea.createSprite(SPRITE_PLAYER, 1);
drawArea.createSprite(SPRITE_ENEMY, '$');
player = new Character(level, &drawArea, 0);
level->draw();
level->addPlayer(player);
level->addEnemies(3); <-------- SKIPS TO THIS FUNC
char key = ' ';
startTime = timeGetTime();
frameCount = 0;
lastTime = 0;
posx = 0;
player->move(0,0);
while (key != 'q')
{
while (!getInput(&key))
{
timerUpdate();
}
level->keyPress(key);
}
delete player;
return true;
}
the function in full is below, note that when i remove this addEnemies function from the main game loop everything runs perfectly fine with no crash, so it has something to do with the upcoming functions.
void Level::addEnemies(int num)
{
int i = num;
while (i > 0)
{
int xpos = int(float(rand() % 100) / 100) * (width - 2) + 1;
int ypos = int(float(rand() % 100) / 100) * (height - 2) + 1;
if (level[xpos][ypos] != TILE_WALL)
{
Enemy *temp = new Enemy(this, drawArea, SPRITE_ENEMY,
(float)xpos, float(ypos));
temp->addGoal(player);
addNPC((Sprite *)temp);
i--;
}
}
}
It gets through this function without any problems it seems.
After this function is goes back to the game loops and executes through fine, goes into timer update without any problems. Here is the timerUpdate function:
void Game::timerUpdate(void)
{
double currentTime = timeGetTime() - lastTime;
if (currentTime < GAME_SPEED)
return;
level->update(); <--------SKIPS TO THIS FUNC
frameCount++;
lastTime = timeGetTime();
}
This is the Level->Update() Func:
void Level::update(void)
{
for (Iter = npc.begin(); Iter != npc.end(); Iter++)
{
(*Iter)->idleUpdate(); <-------------SKIPS TO THIS FUNC
if ((*Iter)->isAlive() == false)
{
Sprite *temp = *Iter;
//kill the enemy
Iter--;
delete temp;
npc.remove(temp);
}
}
}
idleUpdate():
void Enemy::idleUpdate(void)
{
if (goal)
simulateAI(); <------ Goes to this func
}
simulateAI():
void Enemy::simulateAI(void)
{
vector goal_pos = goal->getPosition();
vector direction;
direction.x = goal_pos.x - pos.x;
direction.y = goal_pos.y - pos.y;
float mag = sqrt(direction.x * direction.x + direction.y * direction.y);
direction.x = direction.x / (mag);
direction.y = direction.y / (mag);
if (!move(direction.x, direction.y)) <------ SKIPS TO THIS FUNC
{
while (!move(rand() % 3 - 1, rand() % 3 - 1))
{
}
}
move function:
bool Sprite::move(float x, float y)
{
int xpos = (int)(pos.x +x);
int ypos = (int)(pos.y +y);
if (isValidLevelMove(xpos,ypos)) SKIPS TO THIS FUNC
{
//.....rest not needed
isValidMove func:
bool Sprite::isValidLevelMove(int xpos, int ypos)
{
if (level->level[xpos][ypos] != TILE_WALL) <-------------THIS LINE CRASHES!!
return true;
return false;
}
I really cant figure out where this goes wrong, and why at the end the call stakc shows such high out of bounds numbers for xpos adn ypos.
Here is the full call stack:
#0 00402920 Sprite::isValidLevelMove (this=0x791498, xpos=-2147483648, ypos=-2147483648) (sprite.cpp:95)
#1 00000000 0x00401750 in Enemy::move (this=0x791498, x=-nan(0x400000) (enemy.cpp:21)
#2 00401892 Enemy::simulateAI (this=0x791498) (enemy.cpp:67)
#3 004017E5 Enemy::idleUpdate (this=0x791498) (enemy.cpp:46)
#4 0040226E Level::update (this=0x792e90) (level.cpp:86)
#5 00401CB8 Game::timerUpdate (this=0x28fec0) (game.cpp:93)
#6 00401BB5 Game::run (this=0x28fec0) (game.cpp:54)
#7 0040258D main() (main.cpp:11)
which basically tells me xpos and ypos have been mutilated from somehere in thsi proccess and thats causeing the crash im sure because its way out of bounds from the [30][20] int array of the width and height of the drawengine.
ANOTHER EDIT:
Here is the Sprite class, if it helps, will edit in more if needed.
enum
{
SPRITE_CLASSID,
CHARACTER_CLASSID,
ENEMY_CLASSID
};
struct vector
{
float x;
float y;
};
class Sprite
{
public:
Sprite(Level *l, DrawEngine *de, int s_index, float x = 1, float y = 1, int i_lives = 1);
~Sprite();
vector getPosition(void);
float getX(void);
float getY(void);
virtual void addLives(int num = 1);
int getLives(void);
bool isAlive(void);
virtual void idleUpdate(void);
virtual bool move(float x, float y);
protected:
Level *level;
DrawEngine *drawArea;
vector pos;
int spriteIndex;
int numLives;
int classID;
vector facingDirection;
void draw(float x, float y);
void erase(float x, float y);
bool isValidLevelMove(int xpos, int ypos);
};
anyway any help and i would be sooooo grateful, i know i must seem totally useless, but i rteally am determined to learn, and any help you guys can provide would be priceless!!!!
full code file (codeblocks) : http://www.mediafire.com/?5xz2seadmagbetb
This might not be the actual problem, but could be related. Your code to create a random position within your Level::addEnemies(int num) function will always return 1 for xpos and ypos.
This is because of the way you apply the casts. You seem to miss parenthesis for your final cast to int. I think you want something like this:
int xpos = int((float(rand() % 100) / 100) * (width - 2)) + 1;
Update:
The code causing the crash is located in your simulateAI() function. With:
float mag = sqrt(direction.x * direction.x + direction.y * direction.y);
You calculate the distance between two points, but if the points have the same coordinates, the distance is 0.
Later with: direction.x = direction.x / (mag); you devide by this potential 0 and as a result your coordinates will contain NaN. Within your bool Sprite::move(float x, float y) function you cast these NaNs into an int which will give you some undefined number. With this number you are trying to access your array which will lead to the access violation that crashes your program.
So first thing to do is to check for a zero distance and handle that differently.