SFML texture usage issue - c++

I want to make a tilemap but when I run this code, the tiles becomes white, the texture has a problem. I know it that from the sf::Texture reference here says that the texture must exist in order to that the sprite can use it. But I dont know how to make it possible.
Here's the code:
class Tile
{
private:
sf::Sprite sprite;
sf::Texture tex;
public:
Tile(int x, int y, sf::Texture tex)
{
this->tex = tex;
this->sprite.setTexture(this->tex);
this->sprite.setPosition(x, y);
}
void render(sf::RenderWindow* target)
{
target->draw(this->sprite);
}
class Tilemap
{
private:
Tile tiles[36][64];
sf::Texture tex[4];
public:
//const/dest
Tilemap()
{
this->tex[0].loadFromFile("Resources/Tilemap/Water/water1.png");
int x = -WIDTH+WIDTH/2;
int y = -HEIGTH/2;
for (int i = 0; i < 36; i++)
{
for (int j = 0; j < 64; j++)
{
this->tiles[i][j] = Tile(x, y, this->tex[0]);
x += 60;
}
y += 60;
x = -WIDTH + WIDTH / 2;
}
}
render(sf::RenderWindow* target, sf::Vector2f pos)
{
for (int i = 0; i < 34; i++)
{
for (int j = 0; j < 64; j++)
{
this->tiles[i][j].render(target);
}
}
};
Tilemap map;
map = Tilemap();
Thank you in advance for any answers :)

I have already solved it by myself. Every tile has one sf::Texture tex and every time I need to change it I just load it again from different file.

Related

Remove items from vector on collision?

I am making a simple game in SDL2 where you are a jellyfish who swim around and eat small food. I have done the jellyfish and got some food spread out on the screen. I have a vector with all the foods (which are part of a class called Entity). Every iteration of the game loop I want to iterate through the vector and check if the foods collide with the player, but with what I've got it gets wierd.
The for loop where I create schools of food and then foods who originate from the school position.
std::vector<Entity> entities;
for (int i = 0; i < 30; i++)
{
int foodg_x = rand() % winW;
int foodg_y = rand() % winH;
for (int i = 0; i < 20; i++)
{
Entity entity = Entity(foodTexture, 1, 1, foodg_x + rand() % 100, foodg_y + rand() % 100, 4, 4);
entities.push_back(entity);
}
}
Here is the for loop in the while loop where the iteration happens. Every Entity has an x and a y variable, and the player position is located at plr.x and plr.y. Right now it only checks if the foods are under and left of the player, but nothing disappears:
for (int i = 0; i < entities.size(); i++)
{
if (entities[i].x < plr.x && entities[i].y > plr.y)
{
std::cout << "c";
entities.erase(entities.begin() + i);
}
else
{
i++;
}
SDL_RenderCopy(renderer, entities[i].texture, &entities[i].srcRect, &entities[i].dstRect);
}
What is going on here and how could I fix this?
I fixed it! It had pretty much nothing to do with the loop. The problem was that the x and y variables of the entities were all the same, although not in their respective rects.
This was fixed by changing
Entity::Entity(SDL_Texture* tex, int srcW, int srcH, float x, float y, int w, int h)
{
texture = tex;
x = x;
y = y;
w = w;
h = h;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = srcW;
srcRect.h = srcH;
dstRect.x = x;
dstRect.y = y;
dstRect.w = w;
dstRect.h = h;
}
To:
Entity::Entity(SDL_Texture* tex, int srcW, int srcH, float ex, float ey, int ew, int eh)
{
texture = tex;
x = ex;
y = ey;
w = ew;
h = eh;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = srcW;
srcRect.h = srcH;
dstRect.x = x;
dstRect.y = y;
dstRect.w = w;
dstRect.h = h;
}
If someone could, please explain why this is. :)

Creating a nested for loop clipping function

My old C++ code reads a spritesheet (rows = different buttons, cols = different button states) one row at a time using for loops and divides them into individual sprite clips. All the sprites are the same size. Works fine.
Now, I want to put the elements of the clipping for loops into a struct so that I can write a single clipping function to iterate through every graphic on a given spritesheet. Running into a few problems getting everything to work. Something changed with the enums when I put them in the struct, and I think I screwed up the nested for loop.
Old Code
const int LONGBUTTON_HEIGHT = 128;
const int LONGBUTTON_WIDTH = 256;
enum CreateButtonState { CREATE_DEFAULT, CREATE_HOVER, CREATE_PRESSED, CREATE_TOTAL };
enum CreditsButtonState { CREDITS_DEFAULT, CREDITS_HOVER, CREDITS_PRESSED, CREDITS_TOTAL };
enum MenuButtonState { MENU_DEFAULT, MENU_HOVER, MENU_PRESSED, MENU_TOTAL };
main
{
//Load long button spritesheet texture
SDL_Texture* longbutton_image = loadTexture("longbuttonSpriteSheet.png", renderer);
//Long Buttons
//Create Button Setup
SDL_Rect create_clips[CreateButtonState::CREATE_TOTAL];
for (int i = 0; i < CreateButtonState::CREATE_TOTAL; i++)
{
create_clips[i].x = i * LONGBUTTON_WIDTH;
create_clips[i].y = 0;
create_clips[i].w = LONGBUTTON_WIDTH;
create_clips[i].h = LONGBUTTON_HEIGHT;
}
int useCreate_Clip = CREATE_DEFAULT;
// Credits Button Setup
SDL_Rect credits_clips[CreditsButtonState::CREDITS_TOTAL];
for (int i = 0; i < CreditsButtonState::CREDITS_TOTAL; i++)
{
credits_clips[i].x = i * LONGBUTTON_WIDTH;
credits_clips[i].y = 1 * LONGBUTTON_HEIGHT;
credits_clips[i].w = LONGBUTTON_WIDTH;
credits_clips[i].h = LONGBUTTON_HEIGHT;
}
int useCredits_Clip = CREDITS_DEFAULT;
//Menu Button Setup
SDL_Rect menu_clips[MenuButtonState::MENU_TOTAL];
for (int i = 0; i < MenuButtonState::MENU_TOTAL; i++)
{
menu_clips[i].x = i * LONGBUTTON_WIDTH;
menu_clips[i].y = 2 * LONGBUTTON_HEIGHT;
menu_clips[i].w = LONGBUTTON_WIDTH;
menu_clips[i].h = LONGBUTTON_HEIGHT;
}
int useMenu_Clip = MENU_DEFAULT;
return 0;
}
New Code
int originalspriteH = 128;
int originalspriteW = 256;
int spritesheetcols = 2;
struct Graphic
{
typedef enum buttonstate {DEFAULT, HOVER, PRESSED, TOTAL};
SDL_Rect clip;
int useClip;
};
void clipSprites(int spritesheetcols, int originalspriteH, int originalspriteW, [Graphic &graphic])
{
for (int j =0; j < spritesheetcols; j++)
{
graphic.clip[graphic.buttonstate::TOTAL];
}
for (int i = 0, i < graphic.buttonstate::TOTAL; i++)
{
graphic.clip[i].x = i * originalspriteW;
graphic.clip[i].y = j * originalspriteH;
graphic.clip[i].h = originalspriteW;
graphic.clip[i].w = originalspriteH;
}
graphic.useClip = DEFAULT;
}
main
{
clipSprites(2, 128, 256, [create, credits, menu])
return 0;
}

Objects, In Vector, Being Corrupted In 'For Loop' Initialization

My problem is as follows:
I am designing a small game; however, I have run into a very large problem, which I have been trying to fix for some time now. Essentially, I want to upgrade buildings, if the use has enough points, but the data in the Building objects are being corrupted. The only object which is as it is 'supposed' to be, is the first allocated object in the buildings vector.
Building Class:
When I run the program I am faced with a black screen (meaning it began properly); and when I debug, I get an error like such: Access violation reading location 0x00000008. Meaning a NULL value has been used.
class Building {
public:
int x = 0, y = 0;
vector<int> buildingID;
vector<int> upgradeCost;
int size = 4;
Building(vector<int> buildingID, int x, int y, vector<int> upgradeCost)
: buildingID(buildingID), x(x), y(y), upgradeCost(upgradeCost) { }
virtual void upgrade();
void drawTile(SDL_Rect, SDL_Surface*);
int buildingLevel = 1;
protected:
};
void Building::upgrade() {
if((buildingLevel+1) <= size)buildingLevel += 1;
}
void Building::drawTile(SDL_Rect drawRect, SDL_Surface* drawnTo) {
Tile::Tiles.at(buildingID[buildingLevel - 1]).drawTile(drawRect, drawnTo);
}
The function which generates the buildings:
void Level::generateTerrain() {
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++) {
int tile = rand()%100;
if (tile >= 25 && tile <= 28) this->tiles.at(i + (j*this->width)) = 2;
else if (tile < 24) this->tiles.at(i + (j*this->width)) = 1;
else if (tile == 29) {
this->addBuilding(Building(vector<int>{4, 3, 2, 1}, i * 75, j * 75, vector<int>{1, 1, 1, 1}), i, j);
}
else this->tiles.at(i + (j*this->width)) = 0;
}
}
The function which adds buildings:
void Level::addBuilding(Building building, int x, int y) {
buildings.push_back(building);
tiles.at(x + (y*this->width)) = buildID(building.buildingID[building.buildingLevel-1], &buildings.at(buildings.size()-1));
}
And lastly the function which draws the Tiles/Buildings:
void Level::drawLevel(int x, int y, int width, int height, SDL_Surface* drawnTo, int beginningX, int beginningY) {
SDL_Rect tempRect;
tempRect.w = 75;
tempRect.h = 75;
for (int i = x; i <= (x + width); i++)
for (int j = y; j <= (y + height); j++) {
if (tiles.at(i + (j*this->width)).id == 999999) continue;
tempRect.x = (i*Tile::Tiles.at(tiles.at(i + (j*this->width)).id).tileSurface->w) + beginningX;
tempRect.y = (j*Tile::Tiles.at(tiles.at(i + (j*this->width)).id).tileSurface->h) + beginningY;
Tile::Tiles.at(tiles.at(i + (j*this->width)).id).drawTile(tempRect, drawnTo);
}
}
If you require any more pieces of the code, please just ask.
Thanks for any help given.
The only part of your code that looks suspect to me is how in addBuilding() you are using the address of an element in the vector in the second parameter to buildID(). Vector class re-allocates the memory it uses when it needs to grow in capacity, so existing elements will likely no longer be at the same address your pointers point to when this happens.

How to speed up vector initialization c++

I had a previous question about a stack overflow error and switch to vectors for my arrays of objects. That question can be referenced here if needed: How to get rid of stack overflow error
My current question is however, how do I speed up the initialization of the vectors. My current method currently takes ~15 seconds. Using arrays instead of vectors it took like a second with a size of arrays small enough that didn't throw the stack overflow error.
Here is how I am initializing it:
in main.cpp I initialize my dungeon object:
dungeon = Dungeon(0, &textureHandler, MIN_X, MAX_Y);
in my dungeon(...) constructor, I initialize my 5x5 vector of rooms and call loadDungeon:
Dungeon::Dungeon(int dungeonID, TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
currentRoomRow = 0;
currentRoomCol = 0;
for (int r = 0; r < MAX_RM_ROWS; ++r)
{
rooms.push_back(vector<Room>());
for (int c = 0; c < MAX_RM_COLS; ++c)
{
rooms[r].push_back(Room());
}
}
loadDungeon(dungeonID, textureHandler, topLeftX, topLeftY);
}
my Room constructor populates my 30x50 vector of cells (so I can set them up in the loadDungeon function):
Room::Room()
{
for (int r = 0; r < MAX_ROWS; ++r)
{
cells.push_back(vector<Cell>());
for (int c = 0; c < MAX_COLS; ++c)
{
cells[r].push_back(Cell());
}
}
}
My default cell constructor is simple and isn't doing much but I'll post it anyway:
Cell::Cell()
{
x = 0;
y = 0;
width = 16;
height = 16;
solid = false;
texCoords.push_back(0);
texCoords.push_back(0);
texCoords.push_back(1);
texCoords.push_back(0);
texCoords.push_back(1);
texCoords.push_back(1);
texCoords.push_back(0);
texCoords.push_back(1);
}
And lastly my loadDungeon() function will set up the cells. Eventually this will read from a file and load the cells up but for now I would like to optimize this a bit if possible.
void Dungeon::loadDungeon(int dungeonID, TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
int startX = topLeftX + (textureHandler->getSpriteWidth()/2);
int startY = topLeftY - (textureHandler->getSpriteHeight()/2);
int xOffset = 0;
int yOffset = 0;
for (int r = 0; r < MAX_RM_ROWS; ++r)
{
for (int c = 0; c < MAX_RM_COLS; ++c)
{
for (int cellRow = 0; cellRow < rooms[r][c].getMaxRows(); ++cellRow)
{
xOffset = 0;
for (int cellCol = 0; cellCol < rooms[r][c].getMaxCols(); ++cellCol)
{
rooms[r][c].setupCell(cellRow, cellCol, startX + xOffset, startY - yOffset, textureHandler->getSpriteWidth(), textureHandler->getSpriteHeight(), false, textureHandler->getSpriteTexCoords("grass"));
xOffset += textureHandler->getSpriteWidth();
}
yOffset += textureHandler->getSpriteHeight();
}
}
}
currentDungeon = dungeonID;
currentRoomRow = 0;
currentRoomCol = 0;
}
So how can I speed this up so it doesn't take ~15 seconds to load up every time. I feel like it shouldn't take 15 seconds to load a simple 2D game.
SOLUTION
Well my solution was to use std::vector::reserve call (rooms.reserve in my code and it ended up working well. I changed my function Dungeon::loadDungeon to Dungeon::loadDefaultDungeon because it now loads off a save file.
Anyway here is the code (I got it down to about 4-5 seconds from ~15+ seconds in debug mode):
Dungeon::Dungeon()
{
rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS);
currentDungeon = 0;
currentRoomRow = 0;
currentRoomCol = 0;
}
void Dungeon::loadDefaultDungeon(TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
int startX = topLeftX + (textureHandler->getSpriteWidth()/2);
int startY = topLeftY - (textureHandler->getSpriteHeight()/2);
int xOffset = 0;
int yOffset = 0;
cerr << "Loading default dungeon..." << endl;
for (int roomRow = 0; roomRow < MAX_RM_ROWS; ++roomRow)
{
for (int roomCol = 0; roomCol < MAX_RM_COLS; ++roomCol)
{
rooms.push_back(Room());
int curRoom = roomRow * MAX_RM_COLS + roomCol;
for (int cellRow = 0; cellRow < rooms[curRoom].getMaxRows(); ++cellRow)
{
for (int cellCol = 0; cellCol < rooms[curRoom].getMaxCols(); ++cellCol)
{
rooms[curRoom].setupCell(cellRow, cellCol, startX + xOffset, startY - yOffset, textureHandler->getSpriteWidth(), textureHandler->getSpriteHeight(), false, textureHandler->getSpriteTexCoords("default"), "default");
xOffset += textureHandler->getSpriteWidth();
}
yOffset += textureHandler->getSpriteHeight();
xOffset = 0;
}
cerr << " room " << curRoom << " complete" << endl;
}
}
cerr << "default dungeon loaded" << endl;
}
Room::Room()
{
cells.reserve(MAX_ROWS * MAX_COLS);
for (int r = 0; r < MAX_ROWS; ++r)
{
for (int c = 0; c < MAX_COLS; ++c)
{
cells.push_back(Cell());
}
}
}
void Room::setupCell(int row, int col, float x, float y, float width, float height, bool solid, /*std::array<float, 8>*/ vector<float> texCoords, string texName)
{
cells[row * MAX_COLS + col].setup(x, y, width, height, solid, texCoords, texName);
}
void Cell::setup(float x, float y, float width, float height, bool solid, /*std::array<float,8>*/ vector<float> t, string texName)
{
this->x = x;
this->y = y;
this->width = width;
this->height = height;
this->solid = solid;
for (int i = 0; i < t.size(); ++i)
this->texCoords.push_back(t[i]);
this->texName = texName;
}
It seems wasteful to have so many dynamic allocations. You can get away with one single allocation by flattening out your vector and accessing it in strides:
std::vector<Room> rooms;
rooms.resize(MAX_RM_ROWS * MAX_RM_COLS);
for (unsigned int i = 0; i != MAX_RM_ROWS; ++i)
{
for (unsigned int j = 0; j != MAX_RM_COLS; ++j)
{
Room & r = rooms[i * MAX_RM_COLS + j];
// use `r` ^^^^^^^^^^^^^^^^^^^-----<< strides!
}
}
Note how resize is performed exactly once, incurring only one single allocation, as well as default-constructing each element. If you'd rather construct each element specifically, use rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS); instead and populate the vector in the loop.
You may also wish to profile with rows and columns swapped and see which is faster.
Since it seems that your vectors have their size defined at compile time, if you can use C++11, you may consider using std::array instead of std::vector. std::array cannot be resized and lacks many of the operations in std::vector, but is much more lightweight and it seems a good fit for what you are doing.
As an example, you could declare cells as:
#include <array>
/* ... */
std::array<std::array<Cell, MAX_COLS>, MAX_ROWS> cells;
UPDATE: since a locally defined std::array allocates its internal array on the stack, the OP will experience a stack overflow due to the considerably large size of the arrays. Still, it is possible to use an std::array (and its benefits compared to using std::vector), by allocating the array on the heap. That can be done by doing something like:
typedef std::array<std::array<Cell, MAX_COLS>, MAX_ROWS> Map;
Map* cells;
/* ... */
cells = new Map();
Even better, smart pointers can be used:
#include <memory>
/* ... */
std::unique_ptr<Map> cells;
cells = std::unique_ptr(new Map());

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.