Related
I started Learning C++ yesterday And In that time i was rewriting my java "Falling Sand" Sandbox Game code in C++ using SFML (bit simplified since i don't know C++). but Performance in C++ was much worse in than java, what could be the reason for it, I Know this is very unfocused question, but my code is simple, i probably have a newbie mistakes which should be easy to correct.
#include <SFML/Graphics.hpp>
#include <iostream>
sf::Clock sclock;
const int WIDTH = 1440, HEIGHT = 960;
const char Blank = 0, Sand = 1, Water = 2;
const char* title = "Sandbox Simulation";
char map[WIDTH*HEIGHT];
sf::Vector2i mousePos;
int dist(int x1, int x2, int y1, int y2) {
return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}
int localBrushSize = 48;
short halfBrush = (short)floor(localBrushSize / 2);
char chosen = Sand;
void place() {
int randY = 0;
int randX = 0;
randX = randY = 1;
for (int y = mousePos.y - halfBrush; y <= mousePos.y + halfBrush; y += randY) {
for (int x = mousePos.x - halfBrush; x <= mousePos.x + halfBrush; x += randX) {
int I = x + y * WIDTH;
int distance = dist(mousePos.x, x, mousePos.y, y);
if (distance < halfBrush && I > 0) {
map[I] = chosen;
}
}
}
}
float Delta_Time() {
return sclock.restart().asSeconds();
}
int main() {
map[11111] = 2;
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), title);
sf::Event evnt;
sf::RectangleShape pixel(sf::Vector2f(1.0f, 1.0f));
window.clear();
while (window.isOpen()) {
while (window.pollEvent(evnt)) {
switch (evnt.type) {
case sf::Event::Closed:
window.close();
break;
}
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
mousePos = sf::Mouse::getPosition(window);
place();
}
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int I = x + y * WIDTH;
switch (map[I]) {
case Sand:
pixel.setPosition(x, y);
pixel.setFillColor(sf::Color::Yellow);
window.draw(pixel);
break;
case Water:
pixel.setPosition(x, y);
pixel.setFillColor(sf::Color::Cyan);
window.draw(pixel);
break;
}
}
}
window.display();
}
return 0;
}
You might be able to make a cached / "framebuffer" like this
TOTALLY untested concept code. WIDTH/HEIGHT might be mixed up, endianess is not OK, etc.
sf::Image image;
sf:Image.create(WIDTH, HEIGHT, sf::Color(0, 0, 0));
sf::Sprite sprite;
std::array<sf::Uint8, WIDTH * HEIGHT * 4> pixels; // you can reuse this. The 4 is the size of RGBA
...
for(int y = 0; y < HEIGHT; y++) {
for(int x = 0; x < WIDTH; x++) {
int offset = (x + y * WIDTH) * 4;
pixels[offset] = sf::Color::Yellow.toInteger(); // in case BIG/Litte endian confusion you might have to do the below.
//pixels[offset + 0 ] = 255; // R?
//pixels[offset + 1 ] = 255; // G?
//pixels[offset + 2 ] = 255; // B?
//pixels[offset + 3 ] = 255; // A?
}
}
image.LoadFromPixels(WIDTH, HEIGHT, pixels);
sprite.SetImage(image);
window.Draw(sprite);
window.Display();
I am trying to work out why my code below works as expected in boost 1.61 but not in boost 1.67.
In boost 1.61 the input polygons are correctly combined and the outline polygon displayed.
In boost 1.67, with no code changes, the outline polygon is wrong and incomplete. It's as if it has missing points.
Output images added to help explain. There is also an #define BOOST_1_6_1 I needed to add as boost 1.61 does not seem to automatically add that header file.
Uncomment drawAllPolygons() if you want to see how the input polygons look.
Can anyone help?
#include <iostream>
#include <boost\geometry.hpp>
//#define BOOST_1_6_1
#ifdef BOOST_1_6_1
#include <boost/geometry/geometries/point_xy.hpp>
#endif
typedef boost::geometry::model::d2::point_xy<int> intPt;
typedef boost::geometry::model::polygon<intPt> polygon;
const int GRID_WIDTH = 220;
const int GRID_HEIGHT = 60;
bool canvas[GRID_WIDTH][GRID_HEIGHT];
std::vector<polygon> output;
std::vector<polygon> input;
void initCanvas()
{
for (unsigned int y = 0; y < GRID_HEIGHT; ++y)
{
for (unsigned int x = 0; x < GRID_WIDTH; ++x)
{
canvas[x][y] = false;
}
}
}
void drawGrid()
{
for (unsigned int y = 0; y < GRID_HEIGHT; ++y)
{
for (unsigned int x = 0; x < GRID_WIDTH; ++x)
{
if (canvas[x][y])
{
std::cout << "x";
}
else
{
std::cout << ".";
}
}
std::cout << std::endl;
}
}
polygon setupPolygon(const int startX, const int startY, const int width, const int height)
{
polygon polygon1;
int endX = startX + width;
int endY = startY + height;
for (int x = startX; x <= endX; ++x)
{
intPt pt(x, startY);
polygon1.outer().push_back(pt);
}
for (int y = startY; y <= endY; ++y)
{
intPt pt(endX, y);
polygon1.outer().push_back(pt);
}
for (int x = endX; x >= startX; --x)
{
intPt pt(x, endY);
polygon1.outer().push_back(pt);
}
for (int y = endY; y >= startY; --y)
{
intPt pt(startX, y);
polygon1.outer().push_back(pt);
}
return polygon1;
}
std::vector<polygon> combine(std::vector<polygon> input)
{
bool loop = true;
while (loop)
{
unsigned int before = input.size();
bool exit = false;
for (unsigned int i = 0; i < input.size() && !exit; ++i)
{
for (unsigned int j = i + 1; j < input.size() && !exit; ++j)
{
std::vector<polygon> output;
boost::geometry::correct(input[i]);
boost::geometry::correct(input[j]);
boost::geometry::union_(input[i], input[j], output);
if (i < j)
{
input.erase(input.begin() + j);
input.erase(input.begin() + i);
}
else
{
input.erase(input.begin() + i);
input.erase(input.begin() + j);
}
input.insert(input.begin(), output.begin(), output.end());
exit = true;
}
}
if (before == input.size())
{
loop = false;
}
}
return input;
}
void drawAllPolygons()
{
for (unsigned int i = 0; i < input.size(); ++i)
{
auto outer = input[i].outer();
for (unsigned int i = 0; i < outer.size(); ++i)
{
int x = outer[i].get<0>();
int y = outer[i].get<1>();
canvas[x][y] = true;
}
}
}
void drawCombinedPolygons()
{
for (unsigned int j = 0; j < output.size(); ++j)
{
auto outer = output[j].outer();
for (unsigned int i = 0; i < outer.size(); ++i)
{
int x = outer[i].get<0>();
int y = outer[i].get<1>();
canvas[x][y] = true;
}
}
}
int main()
{
initCanvas();
input.push_back(setupPolygon(40, 10, 50, 15));
input.push_back(setupPolygon(40, 20, 50, 15));
input.push_back(setupPolygon(50, 10, 50, 15));
input.push_back(setupPolygon(60, 30, 50, 15));
output = combine(input);
//drawAllPolygons();
drawCombinedPolygons();
drawGrid();
std::cin.get();
return 0;
}
Boost 1.61 apparently didn't do all the possible optimizations.
There are quite a few misconceptions.
The orientations for the input polygon (rings) are wrong. You can help your self using is_valid and correct. The code below will show it.
The original polygons are expressly created to contain many redundant points. Even if you just "union" one of those with themselves, you can expect the result to be simplified:
Live On Coliru
polygon p = setupPolygon(40, 10, 50, 15);
std::cout << "original: " << bg::wkt(p) << "\n";
multi_polygon simple;
bg::union_(p, p, simple);
std::cout << "self-union: " << bg::wkt(simple) << "\n";
Would print
original: POLYGON((40 10,40 11,40 12,40 13,40 14,40 15,40 16,40 17,40 18,40 19,40 20,40 21,40 22,40 23,40 24,40 25,40 25,41 25,42 25,43 25,44 25,45 25,46 25,47 25,48 25,49 25,50 25,51 25,52 25,53 25,54 25,55 25,56 25,57 25,58 25,59 25,60 25,61 25,62 25,63 25,64 25,65 25,66 25,67 25,68 25,69 25,70 25,71 25,72 25,73 25,74 25,75 25,76 25,77 25,78 25,79 25,80 25,81 25,82 25,83 25,84 25,85 25,86 25,87 25,88 25,89 25,90 25,90 25,90 24,90 23,90 22,90 21,90 20,90 19,90 18,90 17,90 16,90 15,90 14,90 13,90 12,90 11,90 10,90 10,89 10,88 10,87 10,86 10,85 10,84 10,83 10,82 10,81 10,80 10,79 10,78 10,77 10,76 10,75 10,74 10,73 10,72 10,71 10,70 10,69 10,68 10,67 10,66 10,65 10,64 10,63 10,62 10,61 10,60 10,59 10,58 10,57 10,56 10,55 10,54 10,53 10,52 10,51 10,50 10,49 10,48 10,47 10,46 10,45 10,44 10,43 10,42 10,41 10,40 10))
self-union: MULTIPOLYGON(((90 25,90 10,40 10,40 25,90 25)))
As you can see, the result will be simplified to just the obvious corners of the rectangle. However your "drawing" code relies on the redundant points to be present.
This is why you end up with "spotty" results - that seem to miss points, only because your drawing code doesn't draw any lines. It merely draws the (corner) points.
The canvas dimensions are inverted
I think you had your canvas indices consistently swapped around. I'd suggest using more explicit data-structures with (opional) bounds checking to avoid this.
Change to use SVG
If you change the code to output the SVG of the input and output, you will find that the whole thing makes perfect sense. Note that this also takes care to correct the input:
Live On Coliru
#include <iostream>
#include <fstream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
namespace bg = boost::geometry;
typedef bg::model::d2::point_xy<int> intPt;
typedef bg::model::polygon<intPt> polygon;
typedef bg::model::multi_polygon<polygon> multi_polygon;
const int GRID_WIDTH = 220;
const int GRID_HEIGHT = 60;
std::array<std::array<bool, GRID_WIDTH>, GRID_HEIGHT> canvas;
void initCanvas() {
for (auto& row : canvas)
for (auto& cell : row)
cell = false;
}
void drawGrid() {
for (auto& row : canvas) {
for (auto& cell : row)
std::cout << (cell?"x":".");
std::cout << std::endl;
}
}
template <typename R> void valid(R& g) {
std::string reason;
while (!bg::is_valid(g, reason)) {
std::cout << "Not valid: " << reason << "\n";
bg::correct(g);
}
}
polygon setupPolygon(const int startX, const int startY, const int width, const int height) {
polygon::ring_type r;
int const endX = startX + width;
int const endY = startY + height;
for (int x = startX; x <= endX; ++x) r.emplace_back(x, startY);
for (int y = startY; y <= endY; ++y) r.emplace_back(endX, y);
for (int x = endX; x >= startX; --x) r.emplace_back(x, endY);
for (int y = endY; y >= startY; --y) r.emplace_back(startX, y);
valid(r);
return {r};
}
template <typename Polygons>
multi_polygon combine(Polygons const& input) {
if (input.empty()) return {};
//if (input.size()<2) return {input.front()};
multi_polygon out;
for (polygon const& i : input) {
multi_polygon tmp;
bg::union_(out, i, tmp);
out = tmp;
}
return out;
}
void draw(polygon const& p) {
for (auto& pt : p.outer())
canvas.at(pt.y()).at(pt.x()) = true;
}
void draw(multi_polygon const& mp) { for (auto& p : mp) draw(p); }
void draw(std::vector<polygon> const& mp) { for (auto& p : mp) draw(p); }
void writeSvg(multi_polygon const& g, std::string fname) {
std::ofstream svg(fname);
boost::geometry::svg_mapper<intPt> mapper(svg, 400, 400);
mapper.add(g);
mapper.map(g, "fill-opacity:0.5;fill:rgb(0,0,153);stroke:rgb(0,0,200);stroke-width:2");
}
void writeSvg(std::vector<polygon> const& g, std::string fname) {
std::ofstream svg(fname);
boost::geometry::svg_mapper<intPt> mapper(svg, 400, 400);
for (auto& p: g) {
mapper.add(p);
mapper.map(p, "fill-opacity:0.5;fill:rgb(153,0,0);stroke:rgb(200,0,0);stroke-width:2");
}
}
int main() {
std::vector<polygon> input { {
setupPolygon(40, 10, 50, 15),
setupPolygon(40, 20, 50, 15),
setupPolygon(50, 10, 50, 15),
setupPolygon(60, 30, 50, 15),
} };
for (auto& p : input)
valid(p);
auto output = combine(input);
initCanvas();
draw(input);
drawGrid();
initCanvas();
draw(output);
drawGrid();
writeSvg(input, "input.svg");
writeSvg(output, "output.svg");
}
Prints
Not valid: Geometry has wrong orientation
Not valid: Geometry has wrong orientation
Not valid: Geometry has wrong orientation
Not valid: Geometry has wrong orientation
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
........................................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.......................................................................................................................
........................................x.........x.......................................x.........x.......................................................................................................................
........................................x.........x.......................................x.........x.......................................................................................................................
........................................x.........x.......................................x.........x.......................................................................................................................
[ snip ]
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
............................................................................................................................................................................................................................
And the input.svg:
And the output.svg:
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();
#define NOMINMAX // prevent Windows API from conflicting with "min" and "max"
#include <stdio.h> // C-style output. printf(char*,...), putchar(int)
#include <windows.h> // SetConsoleCursorPosition(HANDLE,COORD)
#include <conio.h> // _getch()
/**
* moves the console cursor to the given x/y coordinate
* 0, 0 is the upper-left hand coordinate. Standard consoles are 80x24.
* #param x
* #param y
*/
void moveCursor(int x, int y)
{
COORD c = {x,y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
struct Vec2
{
short x, y;
Vec2() : x(0), y(0) { }
Vec2(int x, int y) : x(x), y(y) { }
void add(Vec2 v)
{
x += v.x;
y += v.y;
}
void operator+=(const Vec2 other_)
{
x += other_.x;
y += other_.y;
};
};
class Rect
{
Vec2 min, max;
public:
Rect(int minx, int miny, int maxx, int maxy)
:min(minx,miny),max(maxx,maxy)
{}
Rect(){}
void draw(const char letter) const
{
for(int row = min.y; row < max.y; row++)
{
for(int col = min.x; col < max.x; col++)
{
if(row >= 0 && col >= 0)
{
moveCursor(col, row);
putchar(letter);
}
}
}
}
bool isOverlapping(Rect const & r) const
{
return !( min.x >= r.max.x || max.x <= r.min.x
|| min.y >= r.max.y || max.y <= r.min.y);
}
void translate(Vec2 const & delta)
{
min += (delta);
max += (delta);
}
void setMin(Vec2 const & min)
{
this->min = min;
}
void setMax(Vec2 const & max)
{
this->max = max;
}
Vec2 getMin()
{
return min;
}
Vec2 getMax()
{
return max;
}
void setRandom(Rect &r)
{
int posX, posY, height, width;
posX = rand() % 51;
posY = rand() % 21;
height = 2 + rand() % 11;
width = 2 + rand() % 11;
height = height / 2;
width = width / 2;
min.x = posX - width;
min.y = posY - height;
max.x = posX + width;
max.y = posY + height;
}
};
int main()
{
// initialization
Rect * userRect = new Rect(7, 5, 10, 9);
Rect rect0(10, 2, 14, 4);
Rect rect1(1, 6, 5, 15);
Rect testSetRandom;
int userInput;
do
{
// draw
rect0.draw('0');
rect1.draw('1');
moveCursor(0, 0); // re-print instructions
printf("move with 'w', 'a', 's', and 'd'");
userRect->draw('#');
// user input
userInput = _getch();
// update
Vec2 move;
switch(userInput)
{
case 'w': move = Vec2( 0,-1); break;
case 'a': move = Vec2(-1, 0); break;
case 's': move = Vec2( 0,+1); break;
case 'd': move = Vec2(+1, 0); break;
}
userRect->draw(' '); // un-draw before moving
userRect->translate(move);
}while(userInput != 27); // escape key
delete userRect;
return 0;
}
// Here is what I am trying to do:
// 3) Random rectangles, by reference and by pointer
// a) create a method with the method signature "void setRandom(Rect & r)".
// This function will give the passed-in Rect object a random location.
// The random x should be between 0 and 50 x. The random y should be
// between 0 and 20. Limit the possible width and height to a minimum of 2
// and a maximum of 10.
// b) test "void setRandom(Rect & r)" on the local Rect object "rect0".
// c) create a method with the method signature
// "void setRandomByPointer(Rect * r)", which functions the same as
// "void setRandom(Rect & r)", except that the argument is
// passed-by-pointer.
// d) test "void setRandomByPointer(Rect * r)" on the local Rect object
// "rect1".
In the comments just above is an explanation of what I'm trying to do. I feel I have over complicated a very simple matter. I want to create a method that takes an object by reference and draws it in a random location. Then I want to do the same thing by pointer. The two signatures I'm starting with is "void setRandom(Rect & r)" and "void setRandomByPointer(Rect * r)". I will test each of them out using the object rect0(10, 2, 14, 4).
void setRandom(Rect& r)
{
int posX, posY, height, width;
posX = rand() % 51;
posY = rand() % 21;
height = 2 + rand() % 11;
width = 2 + rand() % 11;
height = height / 2;
width = width / 2;
r.min.x = posX - width;
r.min.y = posY - height;
r.max.x = posX + width;
r.max.y = posY + height;
}
And with pointer
void setRandom(Rect* r)
{
int posX, posY, height, width;
posX = rand() % 51;
posY = rand() % 21;
height = 2 + rand() % 11;
width = 2 + rand() % 11;
height = height / 2;
width = width / 2;
r->min.x = posX - width;
r->min.y = posY - height;
r->max.x = posX + width;
r->max.y = posY + height;
}
Also this methods don't interact with this object, so they can be declared as static or moved outside of the class.
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!