How should I run game loop and change its objects? - c++

I am writing a game on OpenGL + C++.
The only way to generate animation is define an infinite loop inside glutDisplayFunc. And I did. But imagine I have different gameObjects, and each one has its draw() function.
But I want a function to add another object. As I hold them in array of pointers, this function looks like this:
void display() {
while (running) {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
if(objects_count != 0)
for(unsigned int i = 0; i< objects_count - 1; i++) {
(objects + i)->drawAsPoint();
}
glFlush();
usleep(f60FPS);// Render now
}
}
bool addRenderableObjectSync(GameEntity *object) {
running = false;
if(objects_count + 1>= objects_capacity) {
//reallocating memory
objects_capacity = round(objects_capacity * 1.5f);
GameEntity * _new_objects = new GameEntity[objects_capacity];
for(unsigned int i = 0; i < objects_count; i++) {
_new_objects[i] = objects[i];
}
//now objects are safe to delete free
delete[] objects;
//now we move new array to objects and free the temporary one
objects = _new_objects;
delete[] _new_objects;
}
objects[objects_count] = *object;
objects_count++;
//delete *object as it no longer needed;
delete object;
return true; }
running is a boolean which responses for loop inside display func.
BUT, draw-loop is infinite, i think that i need some async function which can hack running state, for example this:
void Renderer::addRenderableObject(GameEntity *object) {
//the reason we do it cause a game is in infinite loop which never ends
//so we stop the game, add an object, and after fn finish we let it run again
std::future<bool> fut = std:: async(addRenderableObjectSync, object);
running = fut.get();
}
Is that a right solution? Or there are better approaches?

Related

Unexpected behaviour by pointer (when manipulated) but defined behaviour when using a double pointer

I'm quite confused about the reasoning behind the behaviour of pointers to a variable. I would have thought that if I append a pointer to a vector and access it, whether I changed the pointer itself it should still work the same upon the same variable. What I mean by this is, for example, I have a vector of integer pointers and I modify a variable defined somewhere else that has been appended to the vector. If I was to then print them, it should update the vector (not in reality) but it should print the new value of an integer. I'm trying to apply this to an SDL_Texture* in SDL2, however it doesn't quite make sense. In summary, the same concept is applying there however I am using Textures instead. I modify the texture and do "things" with it, but at the end of the loop when I render it, the vector is still iterating to the SDL_Texture* appended to it anyway. What my problem is, is as I change and modify the texture, when I go to render it it doesn't show up. This isn't because the texture isn't properly loaded or anything (I have tested it and rather than using the vector, I draw it raw) but when using the vector it doesn't work properly. Here is the code:
void Main::Mainloop()
{
Screen_Object m_Screen_Object;
TTF_Font* font = TTF_OpenFont("Anonymous_Pro.ttf", 30);
SDL_Surface* surf = TTF_RenderText_Blended(font, "Starting fps", {0,0,0});
SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
SDL_FreeSurface(surf);
SDL_Rect rct = {20, 100, 0,0};
SDL_QueryTexture(tex, NULL, NULL, &rct.w, &rct.h);
m_Screen_Object.Add_Texture(tex);
Uint32 start, finish, counter;
counter = 0;
start = SDL_GetTicks();
finish = SDL_GetTicks();
bool running = true;
while (running)
{
Events::Event_Loop();
if (Events::Quit_Application()){
running = false;
break;
}
///Clear display to color
SDL_SetRenderDrawColor(Render::Get_Renderer(), 0,255,0,255);
SDL_RenderClear(Render::Get_Renderer());
///Do stuff here
m_Screen_Object.Draw_Textures();
finish = SDL_GetTicks();
counter += 2;
if (finish - start >= 500)
{
start = SDL_GetTicks();
SDL_DestroyTexture(tex);
std::string fps = std::to_string(counter);
surf = TTF_RenderText_Blended(font, fps.c_str(), {0,0,0});
tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
SDL_FreeSurface(surf);
SDL_QueryTexture(tex, NULL, NULL, &rct.w, &rct.h);
counter = 0;
}
SDL_RenderPresent(Render::Get_Renderer());
}
SDL_DestroyTexture(tex);
TTF_CloseFont(font);
}
int main(int argc, char* argv[])
{
Main::Mainloop();
return 0;
}
and here is the declaration of Screen_Object:
In the header:
std::vector < SDL_Texture* > m_Textures;
In the .cpp:
void Screen_Object::Add_Texture(SDL_Texture* p_Texture)
{
m_Textures.push_back(p_Texture);
}
void Screen_Object::Draw_Textures()
{
for (unsigned int i=0; i < m_Textures.size(); i++)
{
SDL_RenderCopy(Render::Get_Renderer(), m_Textures[i], NULL, &m_Rect);
}
}
Now this code doesn't work in the way I believe it should since I can't understand why it isn't working, but when I change the vector's type to be SDL_Texture**, the code works fine. What on earth is wrong with the code without the **, I just can't logically understand why it won't work properly
The issue seems to be that you are storing pointers in the vector, but outside the vector you are invalidating the pointer.
void Screen_Object::Add_Texture(SDL_Texture* p_Texture)
{
m_Textures.push_back(p_Texture);
}
void Main::Mainloop()
{
Screen_Object m_Screen_Object;
//...
SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
//...
m_Screen_Object.Add_Texture(tex); // <-- add pointer to vector
//...
tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf); // <-- This changes the pointer, but has no effect on the vector's
//...
So anything after that line, if you access the pointer in m_Textures vector, that pointer in the vector is no longer valid, or worse, it is valid, but points to an old (but still valid) SDL_Texture.
A very simple solution is to ensure that you use the pointer you stored in the m_Textures by obtaining a reference to that pointer:
void Main::Mainloop()
{
Screen_Object m_Screen_Object;
TTF_Font* font = TTF_OpenFont("Anonymous_Pro.ttf", 30);
SDL_Surface* surf = TTF_RenderText_Blended(font, "Starting fps", {0,0,0});
SDL_Texture* tex = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
m_Screen_Object.Add_Texture(tex); // <-- Immediately do this
auto& texRef = m_Screen_Object.back(); // <-- Get a reference to the pointer.
Then you use texRef after that. This is the actual reference to the pointer you added to the vector, and not a copy of the pointer.
Then the loop would be simply:
texRef = SDL_CreateTextureFromSurface(Render::Get_Renderer(), surf);
This changes the actual pointer stored in the vector.

Synchronize functions in Cocos2d-x?

I use cocos2d-x-3.13...
I'm just starting out but I've had problems with something: I have a number of sprites in a vector and every second I move them to each position, the problem arises when I want to delete them, since I have a function that moves them With a loop:
HelloWorldScene.cpp
bool HelloWorld::init() {
...
_enemies.reserve(15);
for (unsigned i = 0; i < 5; i++) {
//Here I create the sprites, and I activate the physics in each one :p
_enemies.push_back(sprite);
}
...
/*
This event checks for each collision, what I do is find the sprite
in my vector and then delete the sprite with "removeFromParent ()",
then delete the sprite from my vector. it's good, no? :v
*/
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = [=](PhysicsContact &contact) {
auto a = contact.getShapeA()->getBody(); //bullet
auto b = contact.getShapeB()->getBody(); //enemy
if (a->getCollisionBitmask() == 2 && b->getCollisionBitmask() == 2) {
a->getNode()->removeFromParent();
auto f = std::find(_enemies.begin(), _enemies.end(), ((cocos2d::Sprite*)b->getNode()));
if (f != _enemies.end()) {
_enemies.at(std::distance(_enemies.begin(), f))->removeFromParent();
_enemies.erase(f);
}
}
return true;
};
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
...
this->schedule(schedule_selector(HelloWorld::moveEnemies), 1.0f);
...
}
void HelloWorld::moveEnemies(float f) {
for (unsigned i = 0; i < _enemies.size(); i++)
{
cocos2d::Vec2 pos = _enemies.at(i)->getPosition(); //This line throws the exception
_enemies.at(i)->setPosition(pos.x + 5, pos.y);
}
}
I have an event that when I press a key, creates a sprite and if it collides with one of the enemies just disappears, everything is fine here, very simple, but when you put the "moveEnemies" function and the sheduler everything is complicated. .. by shooting at one of the enemies quickly throws me an exception (Visual Studio 2015):
"Access violation when reading location 0xDDDDDE35"
I think this occurs because is the vector manipulated at the same time by the event and the "moveEnemies" function?
Is it possible to solve this? Or am I doing something wrong? I would appreciate anyone guiding me ...
I found a simple solution, it is possible that someone will serve you ...
We simply add a scheduler to each sprite and the vector would become obsolete.
auto enemy = cocos2d::Sprite::createWithSpriteFrameName("x.png");
enemy->setPosition(x, y);
enemy->setScale(0.5, 0.5);
//bla bla bla...
enemy->schedule([enemy](float x) {
//Code
auto pos = enemy->getPosition();
enemy->setPosition(pos.x + 10, pos.y);
}, 2.0f, "tag");
It is assumed that deleting the sprite in the event would eliminate the scheduler ...

SFML C++11 can't call the function twice from two different objects of the same type

I am trying to create two RedShip objects. Each of them has a bullets_.create() command which initializes a Bullet object in an array of Bullet objects. I have another similar object called GreenShip. When I instantiate 1 GreenShip and 1 RedShip they both can initialize seperate Bullet objects into the bullets_[] array. But when i have the same object twice only one of them is able to create the Bullet objects even though when debugged they both call the create() method succesfully.
Here is the problematic code:
the ObjectPool class:
class BulletPool{
public:
BulletPool();
void create(sf::Vector2f pos, sf::Color color, float bullet_radius, bool enemy, float velx, float vely);
void update(sf::RenderWindow& window, sf::Vector2f screen);
private:
Bullet* firstAvailable_;
static const int POOL_SIZE = 100;
Bullet bullets_[POOL_SIZE];
};
the BulletPool object constructor:
BulletPool::BulletPool() {
// The first one is available.
firstAvailable_ = &bullets_[0];
// Each particle points to the next. all linked!!
for (int i = 0; i < POOL_SIZE - 1; i++)
{
bullets_[i].setNext(&bullets_[i + 1]);
}
// The last one terminates the list.
bullets_[POOL_SIZE - 1].setNext(NULL);
}
BulletPool update method:
void BulletPool::update(sf::RenderWindow& window, sf::Vector2f screen) {
for (int i = 0; i < POOL_SIZE; i++) {
bullets_[i].update(screen);
if (!bullets_[i].inUse) {
printf("foundavailable:%i\n",i);
bullets_[i].setNext(firstAvailable_);
firstAvailable_ = &bullets_[i];
break;
} else {
std::printf("drawn::%i!\n",i);
window.draw(bullets_[i].shape);
}
}
}
BulletPool.create() which takes in the arguments to be passed to the actual Bullet.init() which initializes the object. The BulletPool.create() is only responsible for placing the new object into the array:
void BulletPool::create(sf::Vector2f pos, sf::Color color, float bullet_radius, bool enemy, float velx, float vely) {
assert(firstAvailable_ != NULL);
printf("new bullet\n");
Bullet* newBullet = firstAvailable_;
firstAvailable_ = newBullet->getNext();
newBullet->init(pos,color,bullet_radius,enemy,velx,vely);
newBullet->inUse=true;
return;
}
Each RedShip object tries to create a Bullet object periodically:
void RedShip::update(int base_time, BulletPool bullets) {
if(base_time%40==0) {
printf("red fire\n");
bullets.create({shape.getPosition().x, shape.getPosition().y + shape.getLocalBounds().height/2}, sf::Color::Red, 4,true, 0,3);
}
}
the console result:
drawn::0!
drawn::1!
foundavailable:2
red fire
new bullet
red fire
new bullet
drawn::0!
drawn::1!
drawn::2!
It should draw 1 more since i am trying to initialize 2 objects.
The Bullet object also has these components as default:
Bullet* getNext() const { return state_.next_; } //to get the next Bullet object in BulletPool
void setNext(Bullet* next) { state_.next_ = next; } //to set the next object
bool inUse{false}; //is the object active?
EDIT
I tried updating the BulletPool after running the create function but have discovered that the first time the method is called, the object is not being initialized into the array. The firstAvaliable stays the same.
foundavailable:4
red fire
new bullet
foundavailable:4
red fire
new bullet
foundavailable:5

c++ Opengl handle elements drawn

I am currently working on a game and I want to know if there is any way of handling with the elements i am drawing . For example : if i draw in a loop 100 cubes , how can i show / hide the cube number 15 or 63 or n ... I thought that initializing elements in a list would work , but i didn't find any property of it that could help.
GLuint cube;
cube = glGenLists(1);
glNewList(cube,GL_COMPILE);
for(int i = -30; i < 3; i++) {
for(int j = -30; j < 3; j++) {
glPushMatrix();
glTranslatef(i*2.0,0,j * 2.0);
Dcube();
glPopMatrix();
}
}
glEndList();
//something like : glDeleteList(cube); but that only works with entire list not with individual objects..
You have a display list, very good. So now you're back to using your regular language primitives to simply call that function.
std::array<bool, 100> cubes;
std::fill(cubes.begin(), cubes.end(), true);
cubes[15] = false;
cubes[63] = false;
for (bool drawCube : cubes) {
if (drawCube) {
// move a bit, perhaps using glTranslate
glCallList(cube);
}
}
OpenGL isn't your statekeeper. It just draws what you tell it to, you're responsible for keeping your objects.

Deletion of std::list causing Access violation

For a school project, my group is using OpenCV to capture video. From these (top-down) images, positions of objects are extracted and turned into a list of Points. Those Points then get triangulated using http://code.google.com/p/poly2tri/ (to overcome the problem of possible non-convex objects). Then, using the coordinates of the triangulated ground pane, we draw the objects in 3D using freeglut. (Side and Top panes are calculated using the ground pane coordinates). The problem we have is that when we delete our old list of Points, the application randomly crashes. Sometimes after 1 second, sometimes after 30 seconds, sometimes after a few minutes. The error we get is "Access violation writing location 0xCCCCCCCC"
Our code:
void WorldLayoutBuilder::update()
{
pointList.clear();
// Capture image
<code to capture image and get countours>
for(size_t i = 0; i < contours.size(); i++)
{
if(contours[i].size() > 50)
{
approxPolyDP(contours[i], approxShape, cv::arcLength(cv::Mat(contours[i]), true)*0.04, true);
drawContours(drawing, contours, i, cv::Scalar(255, 0, 0), 0);
std::vector<Point> newObject;
for(size_t j = 0; j < contours[i].size(); j++)
{
cv::Point newPoint = contours[i][j];
newObject.push_back(Point((float) newPoint.x / 100, 0.0f,(float) newPoint.y / 100));
}
pointList.push_back(newObject);
}
}
ObjectCreator3D::createObjects(&pointList);
contours.clear();
<code to release images, etc>
}
This captures an image, retrieves coordinates of objects, and then calls ObjectCreator3D::createObjects():
void ObjectCreator3D::createObjects(std::list<std::vector<Point>>* inputList)
{
std::list<WorldObject>* tempObjects = new std::list<WorldObject>;
for(std::vector<Point>&pointObject : *inputList)
{
WorldObject worldObject(&pointObject);
tempObjects->push_back(worldObject);
}
DataStorage::getInstance()->setObjects(tempObjects);
}
All objects are turned into WorldObjects:
#include <list>
#include <iostream>
#include <GL/glut.h>
#include <GL/freeglut.h>
#include <time.h>
#include "WorldObject.h"
#include "Point.h"
//Constant height - adjustable/randomized solution is partially implemented in the constructor.
const float WorldObject::HEIGHT = 5.0f;
template <class C> void FreeClear(C & cntr)
{
for(typename C::iterator it = cntr.begin(); it != cntr.end(); ++it)
{
delete * it;
}
cntr.clear();
}
WorldObject::WorldObject(std::vector<Point>* pointList)
{
//TODO, when we have time. Seems difficult because height will change each update...
/*srand (time(NULL));
float fGeneratedY = (rand() % 20 + 2) / 2.0f;*/
cdt = nullptr;
for (Point &point : *pointList)
//point.setY(fGeneratedY);
point.setY(HEIGHT);
this->pointList = pointList;
}
WorldObject::~WorldObject()
{
//Cleanup
delete cdt;
FreeClear(polyPoints);
}
/*
Author Tim Cocu & Bas Rops
Function for drawing the WorldObject
*/
void WorldObject::draw()
{
glPushMatrix();
glColor3f(0.8f, 0.8f, 0.8f);
//Calculate our bottom pane
calculateTriangles();
//BOTTOM PANE
for (unsigned int i = 0; i < calculatedTriangles.size(); i++)
{
p2t::Triangle& t = *calculatedTriangles[i];
p2t::Point& a = *t.GetPoint(0);
p2t::Point& b = *t.GetPoint(1);
p2t::Point& c = *t.GetPoint(2);
glBegin(GL_TRIANGLES);
glNormal3f(0, -1, 0);
glVertex3f((GLfloat)a.x, (GLfloat)0.0f, (GLfloat)a.y);
glVertex3f((GLfloat)b.x, (GLfloat)0.0f, (GLfloat)b.y);
glVertex3f((GLfloat)c.x, (GLfloat)0.0f, (GLfloat)c.y);
glEnd();
}
//TOP PANE
for (unsigned int i = 0; i < calculatedTriangles.size(); i++)
{
p2t::Triangle& t = *calculatedTriangles[i];
p2t::Point& a = *t.GetPoint(0);
p2t::Point& b = *t.GetPoint(1);
p2t::Point& c = *t.GetPoint(2);
glBegin(GL_TRIANGLES);
glNormal3f(0, 1, 0);
glVertex3f((GLfloat)a.x, (GLfloat)HEIGHT, (GLfloat)a.y);
glVertex3f((GLfloat)b.x, (GLfloat)HEIGHT, (GLfloat)b.y);
glVertex3f((GLfloat)c.x, (GLfloat)HEIGHT, (GLfloat)c.y);
glEnd();
}
glColor3f(1.0f, 1.0f, 1.0f);
//SIDE PANES
for(std::size_t iPaneCounter = 0; iPaneCounter < pointList->size(); iPaneCounter++)
{
Point firstPoint = (*pointList)[iPaneCounter];
Point secondPoint (0.0f, 0.0f, 0.0f);
if(iPaneCounter + 1 < pointList->size())
secondPoint.set((*pointList)[iPaneCounter + 1].getX(), (*pointList)[iPaneCounter + 1].getY(), (*pointList)[iPaneCounter + 1].getZ() );
else
secondPoint.set((*pointList)[0].getX(), (*pointList)[0].getY(), (*pointList)[0].getZ());
glBegin(GL_POLYGON);
float fNormalX = (firstPoint.getY() * secondPoint.getZ()) - (firstPoint.getZ() * secondPoint.getY());
float fNormalY = -((secondPoint.getZ() * firstPoint.getX()) - (secondPoint.getX() * firstPoint.getZ()));
float fNormalZ = (firstPoint.getX() * secondPoint.getY()) - (firstPoint.getY() * secondPoint.getX());
glNormal3f(fNormalX, fNormalY, fNormalZ);
glVertex3f(firstPoint.getX(), 0.0f, firstPoint.getZ());
glVertex3f(secondPoint.getX(), 0.0f, secondPoint.getZ());
glVertex3f(secondPoint.getX(), secondPoint.getY(), secondPoint.getZ());
glVertex3f(firstPoint.getX(), firstPoint.getY(), firstPoint.getZ());
glEnd();
}
}
/*
Calculates triangles that make a ground or top pane. Used for calculating possible non-convex objects
*/
void WorldObject::calculateTriangles()
{
//Empty the polyPoints list
if(polyPoints.size() > 0)
FreeClear(polyPoints);
//Convert our Points to p2t::Points
for(std::size_t iBottomIndex = 0; iBottomIndex < pointList->size(); iBottomIndex++)
polyPoints.push_back(new p2t::Point((*pointList)[iBottomIndex].getX(), (*pointList)[iBottomIndex].getZ()));
if(cdt == nullptr)
//Create CDT (Constrained Delaunay Triangulation) and add primary polyPoints
//NOTE: polyPoints must be a simple polygon. The polyPoints' points constitute constrained edges. No repeating points are allowed!
cdt = new p2t::CDT(polyPoints);
//Turn our polyPoints into p2t::Triangles
cdt->Triangulate();
//Set the triangles to use for drawing
calculatedTriangles = cdt->GetTriangles();
}
/*
Retrieve a pointer to a list of Points
*/
std::vector<Point>* WorldObject::getPoints()
{
return pointList;
}
/*
Retrieve a pointer to a list of p2t::Triangles
*/
std::vector<p2t::Triangle*> WorldObject::getCalculatedTriangles()
{
return calculatedTriangles;
}
When all WorldObjects are created, they are stored in DataStorage, DataStorage::getInstance()->setObjects() is called:
void DataStorage::setObjects(std::list<WorldObject>* objectList)
{
delete this->objectList;
this->objectList = objectList;
}
The application seems to crash on delete this->objectList; in setObjects(), so we think the application is trying to delete things he can't delete.
Any help would be greatly appreciated, we've been on this for a few days already
Here, you pass a pointer to an object owned by the list to the constructor of WorldObject:
for(std::vector<Point>&pointObject : *inputList)
{
WorldObject worldObject(&pointObject);
tempObjects->push_back(worldObject);
}
In WorldObject you store the pointer:
//Default Constructor
WorldObject::WorldObject(std::vector<Point>* pointList)
{
float fGeneratedY = (rand() % 20 + 2) / 2.0f;*/
cdt = nullptr;
for (Point &point : *pointList)
point.setY(HEIGHT);
this->pointList = pointList;
}
Which means WorldObject::pointList is only valid so long as the std::list which you constructed your WorldObjects from is still around. (After that, the result is undefined -- it could work, it could crash, it could format your hard drive and leak your identity to Texas).
If you insist on working with raw pointers, you as programmer are responsible for checking and keeping track of the lifetime of every single pointer. This is error prone and will cause random crashes that you will find difficult to track down.
Stop using raw pointers. Instead, if an object owns a resource, store it in a std::unique_ptr<>. If you want the same resource to be shared by multiple objects, use std::shared_ptr and std::weak_ptr, unless the lifetime of all but one of these objects is much, much shorter than the others in a guaranteed way.