Trembling Sprites cocos2dx 3.0 - c++

I notice Sprite trembling while moving down with acceleration.. here is the whole code:
I am testing this code with iPhone 3gs, any guesses why?
TestScene.h :
class TestScene : public Layer {
public:
....
virtual void update(float delta);
float spawnTime = 0.9;
float untilNextSpawn = 0;
virtual void spawn();
float gameSpeed = 200;
float gameAcceleration = 10;
Vector<Sprite*> objects;
};
TestScene.cpp :
void TestScene::update(float delta) {
untilNextSpawn -= delta;
if (untilNextSpawn <= 0) {
spawn();
untilNextSpawn = spawnTime;
}
gameSpeed += gameAcceleration * delta;
for (int i = 0; i < objects.size(); i++) {
Sprite* t = objects.at(i);
if (t->getPosition().y <= 0)
t->setVisible(false);
else
t->setPositionY(t->getPositionY() - gameSpeed * delta);
}
}
void TestScene::spawn() {
Sprite* sprite = NULL;
for (int i = 0; i < objects.size(); i++) {
Sprite* t = objects.at(i);
if (!t->isVisible()) {
sprite = t; break;
}
}
if (!sprite) {
sprite = Sprite::create("prefab_button.png");
this->addChild(sprite);
objects.pushBack(sprite);
}
sprite->setVisible(true);
sprite->setPosition(Point(getContentSize().width / 2, getContentSize().height + 100));
}
prefab_button.png : http://i.imgur.com/oljgyZn.png
TestScene.h : http://pastebin.com/f9sSTLgp
TestScene.cpp : http://pastebin.com/siipLzx6
And here's video: https://www.youtube.com/watch?v=PYfzUnSBQ7M

Related

How should I make my blocks keep their color even if the global color changes?

I am creating a drawing app with oclPixelGameEngine, and I can't figure out how I should make my blocks(pixels) keep their color, even after the global colors change, when I change the global colors now, every already drawn block changes its color.
*EDIT I forgot to mention this code is executed every frame, and the colors aren't, they are defined only at the start, and when I press some key, they change.
I have tried making a variables inside the for loop, still the problem persists.
class Example : public olc::PixelGameEngine
{
public:
Example()
{
sAppName = "RandomStuff";
}
private:
sCell* world;
int nWorldWidth = 16;
int nWorldHeight = 16;
public:
int fColors[12] =
{
255,0,0,
0,255,0,
0,0,255,
255,255,255
};
int rColor = 255;
int gColor = 255;
int bColor = 255;
int fColorsPos = 0;
bool colorsSet = false;
bool OnUserCreate() override
{
// Provede se hned po zapnutí
world = new sCell[nWorldWidth * nWorldHeight];
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
// Zapne se každý snímek
float fBlockWidth = 1.0f;
float fSourceX = GetMouseX();
float fSourceY = GetMouseY();
if (GetMouse(0).bReleased)
{
int i = ((int)fSourceY / (int)fBlockWidth) * nWorldWidth + ((int)fSourceX / (int)fBlockWidth);
world[i].exist = !world[i].exist;
}
if (GetKey(olc::Key::RIGHT).bPressed) {
rColor = fColors[fColorsPos];
gColor = fColors[fColorsPos + 1];
bColor = fColors[fColorsPos + 2];
fColorsPos += 3;
if (fColorsPos > 3*4-1)
fColorsPos = 0;
}
//Renderování
Clear(olc::BLACK);
for (int x = 0; x < nWorldWidth; x++)
for (int y = 0; y < nWorldHeight; y++)
{
if (world[y * nWorldWidth + x].exist)
FillRect(x * fBlockWidth, y * fBlockWidth, fBlockWidth, fBlockWidth,olc::Pixel(rColor, gColor, bColor));
}
return true;
}
};
When I created the for loop color variables, I expected them to stay constant for every block, but the blocks still change their colors.
Perhaps you could implement, as a hack, some 'colorCells' to your code, if sCells does not provide a color Palette. (Although the best implementation is to figure out/add color as variables to the sCell class, for the cleanest code).
class colorCells
{
private:
int rColor = 255;
int gColor = 255;
int bColor = 255;
public:
void setColors(int r, int g, int b) {
rColor = r;
gColor = g;
bColor = b;
}
int getR() {
return rColor;
}
int getG() {
return gColor;
}
int getB() {
return bColor;
}
And you would implement that alongside your code, like so:
class Example : public olc::PixelGameEngine
{
public:
Example()
{
sAppName = "RandomStuff";
}
private:
sCell* world;
colorCell* worldColor;
int nWorldWidth = 16;
int nWorldHeight = 16;
public:
int fColors[12] =
{
255,0,0,
0,255,0,
0,0,255,
255,255,255
};
int rColor = 255;
int gColor = 255;
int bColor = 255;
int fColorsPos = 0;
bool colorsSet = false;
bool OnUserCreate() override
{
// Provede se hned po zapnutí
world = new sCell[nWorldWidth * nWorldHeight];
worldColors = new colorCells[nWorldWidth * nWorldHeight];
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
// Zapne se každý snímek
float fBlockWidth = 1.0f;
float fSourceX = GetMouseX();
float fSourceY = GetMouseY();
if (GetMouse(0).bReleased)
{
int i = ((int)fSourceY / (int)fBlockWidth) * nWorldWidth + ((int)fSourceX / (int)fBlockWidth);
world[i].exist = !world[i].exist;
}
if (GetKey(olc::Key::RIGHT).bPressed) {
rColor = fColors[fColorsPos];
gColor = fColors[fColorsPos + 1];
bColor = fColors[fColorsPos + 2];
if (worldColors[y * nWorldWidth + x].exist) {
worldColors[y * nWorldWidth + x].SetColors(rColor, gColor, bColor);
}
fColorsPos += 3;
if (fColorsPos > 3*4-1)
fColorsPos = 0;
}
//Renderování
Clear(olc::BLACK);
for (int x = 0; x < nWorldWidth; x++)
for (int y = 0; y < nWorldHeight; y++)
{
if (world[y * nWorldWidth + x].exist) {
auto sColor = worldColors[y * nWorldWidth + x];
FillRect(x * fBlockWidth, y * fBlockWidth, fBlockWidth, fBlockWidth,olc::Pixel(sColor.getR(), sColor.getG(), sColor.getB()));
}
}
return true;
}
};
Hopefully that gives you a rough idea about things, the better way to implement it is probably to make colorCells use a tuple to store the colors, but this should be enough to help you understand how to store the colors for now.
Hope that helps!

Removing and re-adding elements to a vector

Hi i'm somewhat new with c++ and openframeworks and I'm trying to get my program running more smooth by erasing the element from the vector when it goes out of the screen. At the moment as the time goes on it still gets slower and slower. I'm breaking my head over this. If you have any idea how I could improve this.
bool isDead(Particle &p) {
if (p.position.x > ofGetWindowWidth() || p.position.x == -1 || p.position.y > ofGetWindowHeight() || p.position.y == -1) {
return true;
}
else {
return false;
}
}
//--------------------------------------------------------------
void ofApp::setup() {
ofSetFrameRate(60);
ofEnableAlphaBlending();
ofBackground(ofColor::black);
for (int i = 0; i < 50; i++) {
Particle p;
p.setup(ofVec2f(ofRandom(ofGetWindowWidth() * 0.45, ofGetWindowWidth() * 0.55), ofRandom(ofGetWindowHeight() * 0.45, ofGetWindowHeight() * 0.55)));
particles.push_back(p);
}
beat1.load("beat1.wav");
beat2.load("beat2.wav");
beat3.load("beat3.wav");
fftSmooth = new float[8192];
for (int i = 0; i < 8192; i++) {
fftSmooth[i] = 0;
}
bands = 128;
beat1.setVolume(0.2);
beat2.setVolume(0.2);
beat3.setVolume(0.2);
}
//--------------------------------------------------------------
void ofApp::update() {
ofSoundUpdate();
float * value = ofSoundGetSpectrum(bands);
for (int i = 0; i < bands; i++) {
fftSmooth[i] *= 0.2f;
if (fftSmooth[i] < value[i]) {
fftSmooth[i] = value[i];
}
}
ofRemove(particles, isDead);
for (Particle& p : particles) {
if (!p.isActive) {
p.position = ofVec2f(ofRandom(ofGetWindowWidth() * 0.45, ofGetWindowWidth() * 0.55), ofRandom(ofGetWindowHeight() * 0.45, ofGetWindowHeight() * 0.55));
p.isActive = true;
return;
}
p.update();
}
if (isDead) {
Particle p;
p.setup(ofVec2f(0, 0));
particles.push_back(p);
}
}
//--------------------------------------------------------------
void ofApp::draw() {
for (Particle& p1 : particles) {
if (!p1.isActive) continue;
bool foundConnection = false;
//search for connections.
for (Particle& p2 : particles) {
if (!p2.isActive || p2.drawPosition == p1.drawPosition) continue;
float distance = p1.drawPosition.distance(p2.drawPosition);
for (int i = 0; i < bands; i++) {
if (distance > 10 && distance < 50 * fftSmooth[i]) {
ofDrawLine(p1.drawPosition, p2.drawPosition);
foundConnection = true;
}
}
}
for (int i = 0; i < 50; i++) {
p1.draw(-(fftSmooth[i] * 10));
}
}
}

How to write an intersection function for any kind of shapes

I have to write a function, which detects intersection and returns true or false.
I have Shape.cpp file, and rectangle.cpp, circle.cpp files inherits of it. I tried to calculate it, but i failed. There is no error, but when my program starts, it crashes. MY Question is why it crashes? is my way wrongs? Here is circle.cpp file.
bool Circ::intersects(Shape* pshape)
{
Rect *p1 = dynamic_cast<Rect*>(pshape);
Circ *p2 = dynamic_cast<Circ*>(pshape);
if(p1)
{
float circleDistance_x = abs(p2->getPos().x - p1->getPos().x);
float circleDistance_y = abs(p2->getPos().y - p1->getPos().y);
if(circleDistance_x > (p1->getSize().x/2 + p2->getRad()))
return false;
if(circleDistance_y > (p1->getSize().y/2 + p2->getRad()))
return false;
if(circleDistance_x <= (p1->getSize().x/2))
return true;
if(circleDistance_y <= (p1->getSize().y/2))
return true;
float cornerDistance_sq = (circleDistance_x - (p1->getSize().x/2)) + (circleDistance_y - (p1->getSize().y/2))*(circleDistance_y - (p1->getSize().y/2));
return (cornerDistance_sq <= p2->getRad()^2);
}
return false;
}
This is not the code all i want to write. But when it fails, i stopped to write.
and my Shapes.h file
#ifndef _SHAPES_H
#define _SHAPES_H
struct Point2d
{
float x, y;
};
struct Point3d
{
float r, g, b;
};
class Shape
{
protected:
bool m_bMarked;
Point3d m_col;
Point2d m_veldir;
Point2d m_pos;
float m_vel;
public:
Shape(Point2d& pos, Point2d& veldir, float vel, Point3d& col)
:m_pos(pos),m_veldir(veldir),m_vel(vel),m_col(col)
{
m_bMarked = false;
}
virtual ~Shape() {}
virtual void draw() = 0;
virtual bool intersects(Shape*) = 0;
inline void move() { m_pos.x += m_veldir.x*m_vel; m_pos.y += m_veldir.y*m_vel; }
inline void invert_xdir() { m_veldir.x *= -1; }
inline void invert_ydir() { m_veldir.y *= -1; }
inline void MarkShape() { m_bMarked = true; }
inline void UnMarkShape() { m_bMarked = false; }
inline bool isMarked() { return m_bMarked; }
inline void increase_vel() { m_vel += 0.01f; }
inline void decrease_vel() { m_vel -= 0.01f; }
};
#endif
And finally my ShapesMain.cpp file
#include <time.h>
#include <GL/glut.h>
#include <cmath>
#include "Rectangle.h"
#include "Circle.h"
// YOU CAN CHANGE THE NUMBER OF SHAPES
#define SHAPE_COUNT 20
// YOU CAN MODIFY WINDOW SIZE BY CHANGING THESE
// YOU MAY ALSO VIEW WINDOW IN FULL SCREEN
#define WINDOWX 500
#define WINDOWY 500
// UNCOMMENT THE LINE BELOW TO STOP MOVING SHAPES
//#define NO_MOTION
// CHANGE THESE DIMENSIONS HOWEVER YOU LIKE
#define MAX_SHAPE_DIM 70
#define MIN_SHAPE_DIM 10
float g_windowWidth = WINDOWX;
float g_windowHeight = WINDOWY;
Shape* g_shapeList[SHAPE_COUNT];
int g_numShapes = 0;
bool g_bShowIntersection = true;
//------------------------------------
void Initialize()
{
srand ( time(NULL) );
// delete previous shapes, if there is any
if (g_numShapes > 0)
{
for (int i = 0; i < g_numShapes; i++)
delete g_shapeList[i];
}
// create a new shape repository
do {
g_numShapes = rand() % SHAPE_COUNT; // number of shapes are randomly determined
} while (g_numShapes < 5); // we dont want to have less than 5 shapes
int rect_count = g_numShapes * (rand() % 10 / 10.0f);
int circle_count = g_numShapes - rect_count;
int half_wind_x = 3* g_windowWidth / 4;
int half_wind_y = 3* g_windowHeight / 4;
int max_dim = MAX_SHAPE_DIM; // max dim. of any shape
int min_dim = MIN_SHAPE_DIM; // min dim. of any shape
int quad_wind = g_windowWidth / 4;
for (int i= 0; i<g_numShapes; i++)
{
float x, y;
float v1, v2;
// set positions
do {
x = rand() % half_wind_x;
} while (x <= quad_wind);
do {
y = rand() % half_wind_y;
} while (y <= quad_wind);
Point2d pos = { x,y };
// set velocity directions
do{
v1 = rand() % 10 / 10.0f;
v2 = rand() % 10 / 10.0f;
} while (v1 == 0 || v2 == 0);
v1 *= (rand() % 2) ? -1 : 1;
v2 *= (rand() % 2) ? -1 : 1;
float vnorm = sqrt(v1*v1 + v2*v2);
Point2d veldir = { v1 / vnorm, v2 / vnorm };
// set velocity
float vel;
do {
vel = rand() % 2 / 10.0f;
} while (vel == 0);
#ifdef NO_MOTION
vel = 0.0f;
#endif
//set color
float R = rand()%100/100.0f;
float G = rand()%100/100.0f;
float B = rand()%100/100.0f;
Point3d color = { R,G,B };
// construct objects
if (i < rect_count)
{
float wx;
float wy;
do {
wx = rand() % quad_wind;
} while (wx < min_dim || wx>max_dim);
do {
wy = rand() % quad_wind;
} while (wy < min_dim || wy>max_dim);
Point2d size = { wx, wy };
Rect* pRect = new Rect(pos, size, veldir, vel, color);
g_shapeList[i] = pRect;
}
else
{
float rad;
do {
rad = rand() % quad_wind;
} while (rad < min_dim || rad>max_dim);
Circ* pCirc = new Circ(pos, rad, veldir, vel, color);
g_shapeList[i] = pCirc;
}
}
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
//-------------------------------------
// This function handles the intersections of shapes.
// if the user is not interested in marking intersections
// s/he can set bMarkIntersections to false..in this case
// no intersection test is performed
void MarkObjects(bool bMarkIntersections)
{
if (bMarkIntersections == false)
{
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->UnMarkShape();
}
else
{
// reset the states of all shapes as unmarked
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->UnMarkShape();
for (int i = 0; i < g_numShapes; i++)
{
for (int j = i+1; j < g_numShapes; j++)
{
if (g_shapeList[i]->intersects(g_shapeList[j]))
{
g_shapeList[i]->MarkShape();
g_shapeList[j]->MarkShape();
}
}
}
}
}
//------------------------------------
void UpdateData()
{
// create viewport bounding rectangles to keep the shapes within the viewport
Point2d Winpos = { -1.0,0.0 };
Point2d Winsize = { 1.0 , g_windowHeight };
Point2d Winveldir = { 0,0 }; // dummy veldir
float Winvel = 0.0f; //not moving
Point3d Wincol = { 0,0,0 }; // dummy color
Rect WindowRectLeft(Winpos, Winsize, Winveldir, Winvel, Wincol);
Winpos.x = 0.0; Winpos.y = -1.0;
Winsize.x = g_windowWidth; Winsize.y = 1.0;
Rect WindowRectBottom(Winpos, Winsize, Winveldir, Winvel, Wincol);
Winpos.x = g_windowWidth; Winpos.y = 0.0;
Winsize.x = 1; Winsize.y = g_windowHeight;
Rect WindowRectRight(Winpos, Winsize, Winveldir, Winvel, Wincol);
Winpos.x = 0.0; Winpos.y = g_windowHeight;
Winsize.x = g_windowWidth; Winsize.y = 1.0f;
Rect WindowRectUp(Winpos, Winsize, Winveldir, Winvel, Wincol);
for (int i = 0; i < g_numShapes; i++)
{
// move the shape
g_shapeList[i]->move();
// if it bounces to the window walls, invert its veldir
if (g_shapeList[i]->intersects(&WindowRectLeft) ||
g_shapeList[i]->intersects(&WindowRectRight))
g_shapeList[i]->invert_xdir();
if (g_shapeList[i]->intersects(&WindowRectBottom) ||
g_shapeList[i]->intersects(&WindowRectUp))
g_shapeList[i]->invert_ydir();
}
}
//------------------------------------
void ChangeSize(GLsizei w, GLsizei h)
{
if(h == 0)
h = 1;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
g_windowHeight = h;
g_windowWidth = w;
glOrtho(0, g_windowWidth, 0, g_windowHeight , 1.0f, -1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//------------------------------------
void processNormalKeys(unsigned char key, int x, int y)
{
if (key == 'q') // PRESS 'q' to terminate the application
exit(0);
if(key=='r') // PRESS 'r' ket to reset the shapes
Initialize();
if (key == 's') // toggle between showing the intersections or not
g_bShowIntersection = g_bShowIntersection ? false: true;
}
//------------------------------------
void processSpecialKeys(int key, int x, int y)
{
switch(key) {
case GLUT_KEY_LEFT :
break;
case GLUT_KEY_RIGHT :
break;
case GLUT_KEY_UP:
// PRESSING UP ARROW KEY INCREASES THE SHAPE VELOCITIES
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->increase_vel();
break;
case GLUT_KEY_DOWN:
// PRESSING DOWN ARROW KEY DECREASES THE SHAPE VELOCITIES
for (int i = 0; i < g_numShapes; i++)
g_shapeList[i]->decrease_vel();
break;
}
}
//-------------------------------------
void display() {
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
UpdateData();
MarkObjects(g_bShowIntersection);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
for (int i= 0; i<g_numShapes; i++)
g_shapeList[i]->draw();
glutSwapBuffers();
}
//------------------------------------
int main(int argc, char* argv[])
{
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );
glutInitWindowPosition(100,100);
glutInitWindowSize(WINDOWX, WINDOWY);
glutCreateWindow("COM102B - PA4");
// Register callback handler for window re-paint
glutDisplayFunc(display);
glutReshapeFunc(ChangeSize);
glutIdleFunc(display);
glutKeyboardFunc(processNormalKeys);
glutSpecialFunc(processSpecialKeys);
Initialize();
glutMainLoop(); // Enter infinitely event-processing loop
return 0;
}
Your problem is in these lines:
Rect *p1 = dynamic_cast<Rect*>(pshape);
Circ *p2 = dynamic_cast<Circ*>(pshape);
Unless you had inherited Rect from Circ or vice versa this is what makes your program crash, you can't cast your pShape to Circ if it is a Rect, so when you pass a Rect object to your function it will correctly cast to Rect* but it will fail with Circ* returning nullptr, so then when you try to access methods from p2 it will crash becouse you are accessing to invalid memory (0x00000000) :
if(p1)
{
float circleDistance_x = abs(p2->getPos().x - p1->getPos().x);
float circleDistance_y = abs(p2->getPos().y - p1->getPos().y);
if(circleDistance_x > (p1->getSize().x/2 + p2->getRad()))
return false;
if(circleDistance_y > (p1->getSize().y/2 + p2->getRad()))
return false;
if(circleDistance_x <= (p1->getSize().x/2))
return true;
if(circleDistance_y <= (p1->getSize().y/2))
return true;
float cornerDistance_sq = (circleDistance_x - (p1->getSize().x/2)) + (circleDistance_y - (p1->getSize().y/2))*(circleDistance_y - (p1->getSize().y/2));
return (cornerDistance_sq <= p2->getRad()^2);
}
So, you could simply just cast the first p1 pointer since the method is from circle, it's obvious that it was called from a Circ Object so there's no need for p2 pointer.
Rect *p1 = dynamic_cast<Rect*>(pshape);
if(p1)
{
float circleDistance_x = abs(getPos().x - p1->getPos().x);
float circleDistance_y = abs(getPos().y - p1->getPos().y);
if(circleDistance_x > (p1->getSize().x/2 + getRad()))
return false;
if(circleDistance_y > (p1->getSize().y/2 + getRad()))
return false;
if(circleDistance_x <= (p1->getSize().x/2))
return true;
if(circleDistance_y <= (p1->getSize().y/2))
return true;
float cornerDistance_sq = (circleDistance_x - (p1->getSize().x/2)) + (circleDistance_y - (p1->getSize().y/2))*(circleDistance_y - (p1->getSize().y/2));
return (cornerDistance_sq <= getRad()^2);
}
Also on the line:
return (cornerDistance_sq <= getRad()^2)
i think you are trying to get the radius square but this wont do it, what it is actually doing is
(cornerDistance_sq <= getRad()) ^ 2
becouse <= has greater precedence to ^, plus ^ is not a square operator it is a bitwise operator. So what you actually want is :
return cornerDistance_sq <= getRad() * getRad();

Enemy Ghost Instances / Non-unique Objects

I'm working on a simple 2D top-down Zelda style game in C++, but I'm having trouble getting multiple instances of an enemy class to spawn in. Whenever I spawn more than one of an enemy, only the first one registers any collision detection; all other enemies seem to be merely visual "ghosts" that are rendered to the screen. When the first enemy dies, the only one that can, then all other "ghosts" disappear along with it.
I've created an enemy manager class that uses a vector list to hold active enemies, check each one's collision against any box passed in, and update/render the enemies.
class cEnemyMgr {
public:
std::vector<cEnemy*> mobList;
cEnemyMgr(){}
~cEnemyMgr(){
for (int i=0; i < mobList.size(); i++) {
mobList[i]->texture.Close();
//delete mobList[i];
}
}
void render() {
for (int i=0; i < mobList.size(); i++) {
mobList[i]->render();
}
}
void update(float dt){
for (int i=0; i < mobList.size(); i++) {
if ( mobList[i]->hp <= 0 ){
mobList[i]->die();
mobList.pop_back();
} else {
mobList[i]->update(dt);
}
}
}
void spawnMob(int x, int y){
cEnemy* pEnemy = new cMeleeEnemy();
pEnemy->init(x, y);
mobList.push_back(pEnemy);
}
cEnemy* checkCollisions(int x, int y, int wd, int ht){
for (int i=0; i < mobList.size(); i++) {
int left1, left2;
int right1, right2;
int top1, top2;
int bottom1, bottom2;
left1 = x;
right1 = x + wd;
top1 = y;
bottom1 = y + ht;
left2 = mobList[i]->pos.x;
right2 = mobList[i]->pos.x + 64;
top2 = mobList[i]->pos.y;
bottom2 = mobList[i]->pos.y + 64;
if ( bottom1 < top2 ) { return NULL; }
if ( top1 > bottom2 ) { return NULL; }
if ( left1 > right2 ) { return NULL; }
if ( right1 < left2 ) { return NULL; }
return mobList[i];
}
}
};
The enemy class itself is pretty basic; cEnemy is the base class, from which cMeleeEnemy is derived. It has the standard hp, dmg, and movement variables so that it can crawl around the screen to try and collide with the player's avatar and also respond to being attacked by the player. All of this works fine, it's just that when I try to have multiple enemies, only the first one spawned in works correctly while the rest are empty shells, just textures on the screen. It doesn't matter if I make explicit calls to spawnMob rapidly in the same block or if I space them out dynamically with a timer; the result is the same. Can anyone point me in the right direction?
--EDIT--
Here's the code the for enemy.h:
#ifndef ENEMY_H
#define ENEMY_H
#include "texture.h"
#include "timer.h"
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
class cEnemy {
public:
int hp;
int dmg;
D3DXVECTOR2 pos;
D3DXVECTOR2 fwd;
D3DXVECTOR2 vel;
D3DCOLOR color;
int speed;
float rotate;
bool hitStun;
float hitTime;
CTexture texture;
virtual void init(int x, int y) = 0;
virtual void update(float dt) = 0;
virtual void die() = 0;
void render(){
texture.Blit(pos.x, pos.y, color, rotate);
}
void takeDamage(int dmg) {
if (hitStun == false){
extern CTimer Timer;
hitTime = Timer.GetElapsedTime();
hp -= dmg;
color = 0xFFFF0000;
hitStun = true;
}
}
void hitStunned(float duration) {
extern CTimer Timer;
float elapsedTime = Timer.GetElapsedTime();
if ( elapsedTime - hitTime > duration ){
color = 0xFFFFFFFF;
hitStun = false;
}
}
};
class cPlayer : public cEnemy {
public:
int facing;
void init(int x, int y);
void update(float dt);
void die();
};
class cMeleeEnemy : public cEnemy {
public:
cMeleeEnemy(){}
~cMeleeEnemy(){
texture.Close();
}
void init(int x, int y);
void update(float dt);
void die();
};
#endif
And enemy.cpp:
#include "enemy.h"
void cPlayer::update(float dt){
// Player Controls
if ( KEY_DOWN('W') ) {
pos.y -= speed * dt;
facing = 0;
} else if( KEY_DOWN('S') ) {
pos.y += speed * dt;
facing = 2;
}
if ( KEY_DOWN('A') ) {
pos.x -= speed * dt;
facing = 3;
} else if( KEY_DOWN('D') ) {
pos.x += speed * dt;
facing = 1;
}
// Hit Recovery
if ( hitStun == true ) {
hitStunned(1.0);
}
}
void cMeleeEnemy::update(float dt){
extern cPlayer player1;
extern int ScreenWd;
extern int ScreenHt;
D3DXVECTOR2 dir;
dir = player1.pos - pos;
D3DXVec2Normalize(&dir, &dir);
//fwd = (fwd * 0.2) + (dir * 0.8);
fwd = dir;
vel = vel + fwd * speed * dt;
pos = pos + vel * dt;
//keep em on screen
if ( pos.x < 0 ) { pos.x = 0; }
if ( pos.x > ScreenWd - 64 ) { pos.x = ScreenWd - 64; }
if ( pos.y < 0 ) { pos.y = 0; }
if ( pos.y > ScreenHt - 64 ) { pos.y = ScreenHt - 64; }
// Hit Recovery
if ( hitStun == true ) {
hitStunned(0.5);
}
}
void cMeleeEnemy::die(){
extern int score;
extern int numMobs;
score += 1;
numMobs -= 1;
//texture.Close();
}
void cPlayer::die(){
extern char gameState[256];
sprintf(gameState, "GAMEOVER");
}
void cMeleeEnemy::init(int x, int y){
hp = 6;
dmg = 1;
speed = 25;
fwd.x = 1;
fwd.y = 1;
vel.x = 0;
vel.y = 0;
pos.x = x;
pos.y = y;
rotate = 0.0;
color = 0xFFFFFFFF;
hitStun = false;
texture.Init("media/vader.bmp");
}
void cPlayer::init(int x, int y){
facing = 0;
hp = 10;
dmg = 2;
color = 0xFFFFFFFF;
speed = 100;
fwd.x = 1;
fwd.y = 1;
vel.x = 0;
vel.y = 0;
pos.x = x;
pos.y = y;
rotate = 0.0;
hitStun = false;
texture.Init("media/ben.bmp");
}
As you can tell, I'm not that experienced yet. This is my first on-your-own project for school. I just have to say I'm a little confused on where I should be closing textures and deleting objects. Thanks for your time, guys!
In your checkCollisions function, you return NULL, or the object at the position of the first index of the enemy vector after every loop.
Therefore, when the first ghost is not hit, the checkCollisions function will return NULL instead of iterating through each of the subsequent ghosts in the vector.
To fix this, change your checkCollisions function to the following:
cEnemy* checkCollisions(int x, int y, int wd, int ht){
for (int i=0; i < mobList.size(); i++) {
int left1, left2;
int right1, right2;
int top1, top2;
int bottom1, bottom2;
left1 = x;
right1 = x + wd;
top1 = y;
bottom1 = y + ht;
left2 = mobList[i]->pos.x;
right2 = mobList[i]->pos.x + 64;
top2 = mobList[i]->pos.y;
bottom2 = mobList[i]->pos.y + 64;
if ( bottom1 < top2 ) { continue; }
if ( top1 > bottom2 ) { continue; }
if ( left1 > right2 ) { continue; }
if ( right1 < left2 ) { continue; }
return mobList[i];
}
return NULL;
}
Hope this helps!
EDIT:
Note that when you are removing an enemy from the list if it's HP is 0 or less, you are using mobList.pop_back(), but this removes the final element from the vector, you should use something like the following to remove the enemy you want from the list:
std::remove_if( mobList.begin(), mobList.end() []( cEnemy* pEnemy )->bool
{
if( pEnemy->hp <= 0 )
{
pEnemy->die();
return true;
}
else
{
pEnemy->update();
return false;
}
});
Problem solved! I replaced the pop_back() with mobList.erase() method.
void update(float dt){
for (int i=0; i < mobList.size(); i++) {
if ( mobList[i]->hp <= 0 ){
mobList[i]->die();
mobList.erase(mobList.begin() + i);
} else {
mobList[i]->update(dt);
}
}
}
Thank you all for your help, it's much appreciated!

Unpredictable pointer behavior

At the moment I am building a cloth physics app using OpenFrameworks. I'm new to C++, for a heads up.
In my app, two 'neighbor' particle objects are passed to a spring object as pointers. As a test, I have the spring object draw lines between the two particles (between their 3d vector positions). For some reason, these lines are different every time I run the program, even though no random values are involved. When I cout the values of the particle positions from the spring struct, they often are ridiculous values like -4.15301e-12. I'm following example code almost verbatim, so I'm not really sure where I'm going wrong.
Here is the example code I'm following:
https://sites.google.com/site/ofauckland/examples/17-cloth-physics
Here is my Spring struct:
#pragma once
#include "ofMain.h"
#include "Particle.h"
struct Spring {
float k, restLength;
Particle *a, *b;
ofVec3f posA, posB;
Spring(Particle *a, Particle *b, float k = .2) : a(a), b(b), k(k) {
restLength = (b->pos - a->pos).length();
}
void update() {
posA = a->pos;
posB = b->pos;
}
void draw() {
ofSetLineWidth(5);
ofSetColor(0, 255, 0);
ofLine(posA.x, posA.y, posB.x, posB.y);
}
};
The particle struct:
#pragma once
#include "ofMain.h"
struct Particle {
ofVec3f pos;
Particle(ofVec3f pos) : pos(pos) {
}
void update() {
}
void draw() {
ofSetColor(ofRandom(255), 0, 0);
ofFill();
ofCircle(pos.x, pos.y, 3);
}
};
And this is where I pass the two particles to the spring as pointers:
#pragma once
#include "ofMain.h"
#include "Particle.h"
#include "Spring.h"
struct Petal {
float maxWidth, spacing;
vector<Particle> particles;
vector<Spring> springs;
Petal(float maxWidth, float spacing) : maxWidth(maxWidth), spacing(spacing) {
setupPoints();
}
void setupPoints() {
float x = 0;
float y = 0;
for(int r = 1; r <= maxWidth; r++) {
x = (int)(r / 2) * -spacing;
y += spacing;
for(int c = 1; c <= r; c++) {
ofVec3f pos = ofVec3f(x, y, 0);
Particle p(pos);
particles.push_back(p);
x+=spacing;
}
}
for(int r = maxWidth; r > 0; r--) {
x = (int)(r / 2) * -spacing;
y += spacing;
for(int c = 1; c <= r; c++) {
ofVec3f pos = ofVec3f(x, y, 0);
Particle p(pos);
particles.push_back(p);
x+=spacing;
}
}
//find neighbors
for(int i = 0; i < particles.size(); i++) {
Spring s(&particles[i], &particles[findNeighbor(i)]);
springs.push_back(s);
}
}
int findNeighbor(int pIndex) {
float leastDist = 0;
float leastDistIndex = 0;
for(int i = 0; i < particles.size(); i++) {
if(i != pIndex) {
float distance = particles[pIndex].pos.distance(particles[i].pos);
if(abs(distance) < abs(leastDist) || leastDist == 0) {
leastDist = distance;
leastDistIndex = i;
}
}
}
return leastDistIndex;
}
void update() {
for(int i = 0; i < particles.size(); i++) {
particles[i].update();
}
for(int s = 0; s < springs.size(); s++) {
springs[s].update();
}
}
void draw() {
for(int i = 0; i < particles.size(); i++) {
particles[i].draw();
}
for(int s = 0; s < springs.size(); s++) {
springs[s].draw();
}
}
};
This is what happens. What's strange is that some of the springs seem to be in the correct position.
Please let me know if I can clarify something.
Thanks!
Your particles vector holds Particles by value, and the vector can copy and move these values around. When you pass Particles as pointers to the Spring, you are passing the address of something that might not be there at some point in the future. I am not sure if this is the problem, but it certainly is something that needs fixing.