Hey, over the past few days I've been writing a small engine to render blocks to the screen along with lighting but I've encountered a weird bug which causes a block after several animated blocks to become glitched:
I really can't find what's causing the problem and it's annoying me quite a bit...
Here's the source (it's quite messy):
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <fstream>
#include <sstream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
using namespace std;
int nolight = 0;
//String to int conversion function
int StringToInt(string Number)
{
return atoi(Number.c_str());
}
//Int to string conversion function
template <typename T>
string IntToString ( T Number )
{
ostringstream ss;
ss << Number;
return ss.str();
}
struct Position //A position struct for handling all variables to do with position
{
int x = 0, y = 0;
};
struct WindowSettings //A structure for holding window settings such as its name and size
{
const string windowName = "Test";
const int Width = 1024; //32 across
const int Height = 512; //16 up
};
class PlayerClass //The player class for handling the player
{
private:
int Health = 0; //Player health variable
int x = 0, y = 0; //Player position variables (x,y)
public:
void setHealth(int Health); //Setting player health
int getHealth(); //Getting player health
void setPosition(Position NewPosition); //Set the players position using position struct.
Position getPosition(); //Get the players position using position struct
};
void PlayerClass::setHealth(int NewHealth) //Setting player health
{
Health = NewHealth;
}
int PlayerClass::getHealth() //Getting player health
{
return Health;
}
Position PlayerClass::getPosition() //Get the players position
{
Position ReturnValue;
ReturnValue.x = x;
ReturnValue.y = y;
return ReturnValue;
}
void PlayerClass::setPosition(Position NewPosition) //Set the players position
{
x = NewPosition.x;
y = NewPosition.y;
}
class MapClass //Class for handling the map, blocks and textures etc.
{
private:
WindowSettings WINDOW_SETTINGS;
vector<bool> isVisible; //Decides if a block should be rendered or not
vector<sf::RectangleShape> Blocks; //A vector to hold the block shapes
vector<double> heightMap; //A heightmap to decide what height mobs/the player should be on it
vector<sf::Texture> blockTextures; //A vector for holding block textures
vector<string> loadedTextures; //A vector containing all loaded textures to prevent them from being loaded multiple times
vector<int> BlockHeights; //Block heights for heightmap calculation
vector<int> BlockWidths; //Block widths for heightmap calculation
vector<int> TextureID; //A reference to the ID of the texture used on a block
vector<string> TextureFP;
vector<sf::Clock> AnimationClock;
vector<sf::Time> AnimationSwitchTime;
vector<int> animatedBID;
vector<int> AnimationPhase;
vector<int> MaxAnimationPhase;
vector<int> MinAnimationPhase;
vector<sf::Time> CurrentAnimationTime;
vector<bool> Animated; //True/false depending on if the block is animated
public:
sf::RectangleShape *BlockReference(int BlockID);
int loadTexture(string FileName); //A function for loading textures into memory
void setVisibility(int BlockID, bool Visible); //Setting a block to render/not render
int setBlockTexture(string Texturename, int BlockID); //Texture a block
int createBlock(bool Visibility, string FileName, int Height, int Width, bool Animated, sf::Time AnimationSwitchTime,int StartPhase, int MaxPhase); //Create a new block
void setBlockPos(int BlockID, Position BlockPosition); //Set a blocks position
Position getBlockPos(int BlockID); //Return a blocks position in a POSITION structure.
bool isBlockVisible(int BlockID); //Returns true if a block is visible, false if the block is not
bool loadMap(string FilePath); //Loads a map automatically, creates all of the blocks, entity's and lights.
int BlockCount(); //Returns number of loaded blocks
void updateAnimatedBlocks();
void reTextureBlocks();
};
int MapClass::BlockCount()
{
return isVisible.size();
}
sf::RectangleShape *MapClass::BlockReference(int BlockID)
{
return &Blocks[BlockID];
}
void MapClass::reTextureBlocks()
{
for(int a = 0; a < (int)Blocks.size(); a++)
{
Blocks[a].setTexture(&blockTextures[TextureID[a]]);
}
}
int MapClass::loadTexture(string FileName) //Load a texture into memory
{
for(int a = 0; a < (int)loadedTextures.size(); a++)
{
if(loadedTextures[a] == FileName)
{
return a; //If the texture is already loaded then return that textures ID
}
}
blockTextures.push_back(sf::Texture());
if (!blockTextures[blockTextures.size()-1].loadFromFile(FileName+".png"))
{
cout << "\nCould not load block texture!" << endl;
blockTextures.pop_back();
return -1;
}
loadedTextures.push_back(FileName);
for(int a = 0; a < (int)Blocks.size(); a++)
{
Blocks[a].setTexture(&blockTextures[TextureID[a]]);
}
return blockTextures.size()-1;
}
void MapClass::setVisibility(int BlockID, bool Visible) //Set a blocks visibility to true/false
{
if(!BlockID > isVisible.size())
{
if(Visible)
isVisible[BlockID] = true;
else
isVisible[BlockID] = false;
}
}
int MapClass::setBlockTexture(string Texture, int BlockID) //Set a blocks texture to something
{
int TextureID2 = loadTexture(Texture);
Blocks[BlockID].setTexture(&blockTextures[TextureID2]);
return TextureID2;
}
Position MapClass::getBlockPos(int BlockID)
{
Position BP;
BP.x = Blocks[BlockID].getPosition().x;
BP.y = Blocks[BlockID].getPosition().y;
return BP;
}
bool MapClass::isBlockVisible(int BlockID)
{
if(!isVisible[BlockID])
{
return false;
}
Position BlockPosT;
BlockPosT = getBlockPos(BlockID);
if((BlockPosT.x > WINDOW_SETTINGS.Width && BlockPosT.x > WINDOW_SETTINGS.Width - BlockWidths[BlockID]) || (BlockPosT.x < 0 && BlockPosT.x < Blocks[BlockID].getPosition().x + BlockWidths[BlockID]))
{
if((BlockPosT.y > WINDOW_SETTINGS.Height && BlockPosT.y > WINDOW_SETTINGS.Height - BlockHeights[BlockID]) || (BlockPosT.y < 0 && BlockPosT.y < Blocks[BlockID].getPosition().y + BlockHeights[BlockID]))
{
return false;
}
else
{
return true;
}
}
else
{
return true;
}
}
int MapClass::createBlock(bool Visible, string TextureName, int Height, int Width, bool AnimatedS, sf::Time SwapTime,int StartPhase, int MaxPhase) //Create a new block and set its texture
{
TextureFP.push_back(TextureName);
TextureID.push_back(0);
isVisible.push_back(Visible);
Blocks.push_back(sf::RectangleShape(sf::Vector2f(Height,Width)));
BlockHeights.push_back(Height);
BlockWidths.push_back(Width);
int id = setBlockTexture(TextureName, Blocks.size()-1);
TextureID[Blocks.size()-1] = id;
setBlockTexture(TextureName, Blocks.size()-1);
Animated.push_back(AnimatedS);
if(AnimatedS == true)
{
AnimationClock.push_back(sf::Clock());
AnimationSwitchTime.push_back(sf::Time(SwapTime));
CurrentAnimationTime.push_back(sf::Time());
animatedBID.push_back(Blocks.size()-1);
AnimationPhase.push_back(0);
MaxAnimationPhase.push_back(MaxPhase);
MinAnimationPhase.push_back(StartPhase);
}
return Blocks.size()-1;
}
void MapClass::setBlockPos(int BlockID, Position NewPos) //Set a blocks position
{
Blocks[BlockID].setPosition(NewPos.x, NewPos.y);
}
MapClass Map; //In charge of map management
class LightingClass //Manage block lighting
{
private:
int lightLevel = 0;
vector<Position> lightPos;
vector<int> lightStrengh;
public:
int getLightLevel();
void setLightLevel(int Level);
int calculateLighting(int BlockID);
void addLight(Position LightPosition, int LightLevel);
};
void LightingClass::addLight(Position LightPos, int LL)
{
lightPos.push_back(LightPos);
lightStrengh.push_back(LL);
}
int LightingClass::calculateLighting(int Bid)
{
int closestLightDistance = 99999999;
int tempDistance = 0;
Position blockpos = Map.getBlockPos(Bid);
int ClosestLightID = 0;
for(int b = 0; b < (int)lightPos.size(); b++)
{
tempDistance = (std::abs(blockpos.x - lightPos[b].x) + std::abs(blockpos.y - lightPos[b].y));
if(tempDistance < closestLightDistance)
{
closestLightDistance = tempDistance;
ClosestLightID = b;
}
}
if(closestLightDistance <= 128)
{
int LightDensity = 0;
if(closestLightDistance <= 32)
{
LightDensity = 120+(lightStrengh[ClosestLightID]*4);
}
else if(closestLightDistance <= 64)
{
LightDensity = 80+(lightStrengh[ClosestLightID]*4);
}
else if(closestLightDistance <= 96)
{
LightDensity = 60+(lightStrengh[ClosestLightID]*2);
}
else if(closestLightDistance <= 128)
{
LightDensity = 40+(lightStrengh[ClosestLightID]*2);
}
return LightDensity;
}
return 20; //Return a black value by default if the block is not within light range of an object
}
void LightingClass::setLightLevel(int ll)
{
lightLevel = ll;
}
int LightingClass::getLightLevel()
{
return lightLevel;
}
LightingClass Lighting; //Handles block lighting
bool MapClass::loadMap(string FilePath)
{
cout << "\nLoading " << FilePath << "... " << endl;
///Load the map file into memory
ifstream File;
string Buffer;
string Line = "";
vector<string> MapL;
File.open(FilePath);
if(File.is_open())
{
while(!File.eof()) //While there's data to read. Using eof doesn't matter here
{
getline(File, Line); //Get the file line and store it in line
for(int charPos = 0; charPos < (int)Line.size(); charPos++)
{
if(Line[charPos] == ',') //Remove commas and split each into a vector
{
MapL.push_back(Buffer);
Buffer = "";
}
else
{
Buffer += Line[charPos];
}
}
}
MapL.push_back(Buffer); //Ensure that last line of the map is processed
Buffer.clear(); //Clear the buffer
}
else
{
cout << "\nCould not find map: " << FilePath << endl;
return false;
}
File.close();
///Process the map and register any blocks, light-sources or entity's
bool blocksProcessed = false, lightsProcessed = false, entitysProcessed = false;
int blockX = 0, blockY = 0; //Current block to create
string TexturePath = "";
Position blockpos;
int bid = 0; //Temporarily stores the block id of a created block for placing
for(int a = 0; a < (int)MapL.size(); a++)
{
if(blocksProcessed == false && lightsProcessed == false) //Load lights onto the screen
{
if(MapL[a] == "Blocks:")
{
a++;
lightsProcessed = true;
}
else if(StringToInt((string)MapL[a]) == 1)
{
int lightx = 0, lighty = 0, intensity = 0;
lightx = StringToInt((string)MapL[a+1]);
lighty = StringToInt((string)MapL[a+2]);
intensity = StringToInt((string)MapL[a+3]);
Position lightpos;
lightpos.x = lightx;
lightpos.y = lighty;
Lighting.addLight(lightpos, intensity);
a+=3;
nolight++;
}
}
if(lightsProcessed == true && blocksProcessed == false) //Loads blocks onto the screen
{
sf::Time SwitchTime;
TexturePath = "";
blockpos.x = blockX, blockpos.y = blockY;
switch(StringToInt((string)MapL[a]))
{
case 0: //Air
break;
case 1: //stone
TexturePath = "blocks\\stone";
bid = Map.createBlock(true, TexturePath, 32 , 32, false, sf::Time(), 0,0);
break;
case 2: //water
TexturePath = "blocks\\water_still";
SwitchTime = sf::milliseconds(100);
bid = Map.createBlock(true, TexturePath, 32 , 32, true, SwitchTime, 1,16);
break;
case 3: //lava
TexturePath = "blocks\\lava_still";
SwitchTime = sf::milliseconds(100);
bid = Map.createBlock(true, TexturePath, 32 , 32, true, SwitchTime, 1,10);
break;
case 4: //grass
TexturePath = "blocks\\grass_side";
bid = Map.createBlock(true, TexturePath, 32 , 32, false, sf::Time(), 0,0);
break;
}
}
if(TexturePath != "")
{
Map.setBlockPos(bid, blockpos);
}
blockX += 32;
if(blockX == 1024)
{
blockY += 32;
blockX = 0;
}
if(blockY == 512)
{
blockY = 0;
}
}
return true;
}
void MapClass::updateAnimatedBlocks()
{
for(int a = 0; a < (int)animatedBID.size(); a++)
{
CurrentAnimationTime[a] = AnimationClock[a].getElapsedTime();
if(CurrentAnimationTime[a] >= AnimationSwitchTime[a])
{
if(AnimationPhase[a] >= MaxAnimationPhase[a]-1)
{
AnimationPhase[a] = MinAnimationPhase[a]-1;
}
else
{
AnimationPhase[a]++;
}
int yloc = AnimationPhase[a] * 32;
sf::IntRect tsize(0, yloc, 32, 32); //Set the portion of the texture to be visible
Blocks[animatedBID[a]].setTextureRect(tsize);
setBlockTexture(TextureFP[animatedBID[a]], animatedBID[a]);
AnimationClock[a].restart();
}
}
}
int main()
{
PlayerClass Player; //In charge of player functions
WindowSettings Settings; //Declares a structure containing details about the window
//Creating our enviroment
Map.loadMap("map.txt");
if(nolight != 0)
{
for(int a = 0; a < Map.BlockCount(); a++)
{
int LightLevel = Lighting.calculateLighting(a);
sf::RectangleShape& TempBlock = *Map.BlockReference(a);
sf::Color bc = TempBlock.getFillColor();
TempBlock.setFillColor(sf::Color(bc.r,bc.g,bc.b, LightLevel));
}
}
sf::RenderWindow window(sf::VideoMode(Settings.Width,Settings.Height), Settings.windowName); //Creating the window
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event)) //The event loop
{
if(event.type == sf::Event::Closed) //if window close button is pressed then:
{
window.close();
}
}
window.clear(sf::Color::Black); //Clear the window with a black colour to erase previous frame
for(int a = 0; a < Map.BlockCount(); a++)
{
Map.updateAnimatedBlocks();
sf::RectangleShape &blockToDraw = *Map.BlockReference(a);
window.draw(blockToDraw);
}
window.display(); //Finally display the window
}
return 0;
}
[/code=cpp]
The map file looks like this:
[code]
Blocks:,
2,2,2,2,2,2,2,4,4,4,4,4,4,
END_OF_FILE,
What's causing this?
Any help is appreciated - Cl9.