Generating multiple amounts of the same sprite broken - c++

I have just started working on a 2d game of mine. I'm currently experiencing a problem where I am trying to make some land with a grass block texture by spawning the same sprites multiple times with the same grass texture. However, instead of getting a row of the same texture, I am getting the row being stretched for some reason.
Let me show you:
Normal grass texture:
Current grass output:
This is the code:
#include <iostream>
#include <vector>
#include <random>
#include <SFML/Graphics.hpp>
using namespace std;
using namespace sf;
int main() {
RenderWindow window(VideoMode(1920, 1080), "Treko");
Color bg(0, 205, 255);
Font errorFont;
Texture grass;
Texture dirt;
if (!errorFont.loadFromFile("font/Arial/Arial.ttf")) {
RenderWindow error(VideoMode(600, 500), "Error!");
Text errorText;
errorText.setFont(errorFont);
errorText.setString("Unfortunaetly, we are unable to find Arial.ttf.\nPlease reinstall the application and report the error. Thank you.");
errorText.setCharacterSize(18);
errorText.setFillColor(Color::White);
while (error.isOpen()) {
Event errorEvent;
while (error.pollEvent(errorEvent)) {
if (errorEvent.type == Event::Closed) {
error.close();
}
}
error.clear();
error.draw(errorText);
error.display();
}
}
if (!dirt.loadFromFile("img/Dirt.png")) {
RenderWindow error(VideoMode(600, 500), "Error!");
Text errorText;
errorText.setFont(errorFont);
errorText.setString("Unfortunaetly, we are unable to find Dirt.png.\nPlease reinstall the application and report the error. Thank you.");
errorText.setCharacterSize(18);
while (error.isOpen()) {
Event errorEvent;
while (error.pollEvent(errorEvent)) {
if (errorEvent.type == Event::Closed) {
error.close();
}
}
error.clear();
error.draw(errorText);
error.display();
}
}
if (!grass.loadFromFile("img/Grass.png")) {
RenderWindow error(VideoMode(600, 500), "Error!");
Text errorText;
errorText.setFont(errorFont);
errorText.setString("Unfortunaetly, we are unable to find Grass.png.\nPlease reinstall the application and report the error. Thank you.");
errorText.setCharacterSize(18);
errorText.setFillColor(Color::White);
while (error.isOpen()) {
Event errorEvent;
while (error.pollEvent(errorEvent)) {
if (errorEvent.type == Event::Closed) {
error.close();
}
}
error.clear();
error.draw(errorText);
error.display();
}
}
Sprite grassBlock;
grassBlock.setTexture(grass);
Sprite dirtBlock;
dirtBlock.setTexture(dirt);
vector<Sprite> grassM;
/* This is a work in progress. I'm trying to add height to the land using random y axis generation. Any of you know how to do this it would be great to let me know!
std::uniform_int_distribution<int> randomColorRange(680, 720);
std::random_device rd;
std::mt19937 RandYPos(rd());
*/
//THE PROBLEM PART 1
for (int i = 0; i <= 1918; i++) {
grassBlock.setPosition(Vector2f(i + 1, 690));
grassBlock.setScale(Vector2f(0.5f, 0.5f));
grassM.push_back(Sprite(grassBlock));
}
while (window.isOpen()) {
Event event;
while (window.pollEvent(event)) {
if (event.type == Event::Closed) {
window.close();
}
}
window.clear(bg);
//THE PROBLEM PART 2
for (int i = 0; i < grassM.size(); i++) {
window.draw(grassM[i]);
}
window.display();
}
return 0;
}
If anyone knows how to do this, that would be great!

While I didn't try to run the code, to me it looks like everything is working. Your problem is, that you're just moving the sprites one pixel apart:
grassBlock.setPosition(Vector2f(i + 1, 690));
Since all show the same texture with the same origin, this will result in all the sprites essentially showing the first column of your texture (since the rest is overlapped by the next tile).
While I think you should rework your whole code structure (it feels really messy and I wouldn't recommend to create new SFML windows if you encounter errors, since that might fail as well), all you have to do is move your sprites apart by multiplying the index used for your coordinate by the desired tile width:
grassBlock.setPosition(Vector2f(i * tile_width, 690));

Related

The texture of the player does not changing

In the code I wrote, the collision is detected correctly, but only 1 enemy changes players texture. What is the reason and how can I change players texture for every enemy? The problem is between lines 56 and 64 of the code.
Screenshots :
No collision
Collision but texture isn't change
Collision and players texture is changed
My code :
#include <iostream>
#include <stdlib.h>
#include <SFML/Graphics.hpp>
using namespace sf;
int main(){
RenderWindow window(VideoMode(500,500), "Game");
window.setFramerateLimit(100);
Texture playerTexture;
playerTexture.loadFromFile("./images/player.png");
Texture collisionPlayerTexture;
collisionPlayerTexture.loadFromFile("./images/collision_player.png");
Texture enemyTexture;
enemyTexture.loadFromFile("./images/enemy.png");
Sprite player;
player.setTexture(playerTexture);
player.setTextureRect(IntRect(50,50,50,50));
player.setPosition(225,225);
Sprite enemies[5] = {Sprite(),Sprite(),Sprite(),Sprite(),Sprite()};
for(int i=0;i<5;i++){
enemies[i].setTexture(enemyTexture);
enemies[i].setTextureRect(IntRect(25,25,25,25));
enemies[i].setPosition(rand() % 475,rand() % 475);
}
while(window.isOpen()){
Event event;
while(window.pollEvent(event)){
switch(event.type){
case Event::Closed:
window.close();
}
}
if(Keyboard::isKeyPressed(Keyboard::W) || Keyboard::isKeyPressed(Keyboard::Up)){
player.move(0.0f,-1.5f);
}
if(Keyboard::isKeyPressed(Keyboard::S) || Keyboard::isKeyPressed(Keyboard::Down)){
player.move(0.0f,1.5f);
}
if(Keyboard::isKeyPressed(Keyboard::A) || Keyboard::isKeyPressed(Keyboard::Left)){
player.move(-1.5f,0.0f);
}
if(Keyboard::isKeyPressed(Keyboard::D) || Keyboard::isKeyPressed(Keyboard::Right)){
player.move(1.5f,0.0f);
}
window.clear(Color(255,255,255));
//////////////////////////////////////////////This is where I'm talking about//////////////////////////////////////////////
for(int i=0;i<5;i++){
if(player.getGlobalBounds().intersects(enemies[i].getGlobalBounds())){
std::cout << "Collision"; // This is working
player.setTexture(collisionPlayerTexture); // But this line only works for an enemy
} else {
player.setTexture(playerTexture);
}
window.draw(enemies[i]);
}
//////////////////////////////////////////////This is where I'm talking about//////////////////////////////////////////////
window.draw(player);
window.display();
}
return 0;
}
In your loop
for(int i=0;i<5;i++){
you are iterating through all 5 enemies.
If you detect collision with the LAST one, you change the texture and get out of the loop. But if the collision was with any other enemy, the next loop iteration will revert the texture back.
Solution: you might want to break; out of the loop if a collision was detected.

animation of sfml by using array of objects

How to animate sprite with this code?I know what i should to add time delay,but how do it? I use array of objects to they's fast changing. or is it an irrational way of animating?
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace std;
sf::RenderWindow window(sf::VideoMode(600, 400), "!!!");
void animation()
{
sf::Texture arrayOfTexture[9];
sf::Sprite imageOfLamp;
arrayOfTexture[0].loadFromFile("1.png");
arrayOfTexture[1].loadFromFile("2.png");
arrayOfTexture[2].loadFromFile("3.png");
arrayOfTexture[3].loadFromFile("4.png");
arrayOfTexture[4].loadFromFile("5.png");
arrayOfTexture[5].loadFromFile("6.png");
arrayOfTexture[6].loadFromFile("7.png");
arrayOfTexture[7].loadFromFile("8.png");
arrayOfTexture[8].loadFromFile("9.png");
for(;;)
{
for(int i= 0;i <=8;i++)
{
imageOfLamp.setTexture(arrayOfTexture[i]);
window.draw(imageOfLamp);
}
for(int i =8;i >=0;i--)
{
imageOfLamp.setTexture(arrayOfTexture[i]);
window.draw(imageOfLamp);
}
}
}
As said in the comments. For the animation you need to capture the time. Or you will not be able to calculate the duration per frame. And you need to store the current frame in a variable. I put some comment in the example code below. It is not a class anymore because I think we need a running code block here.
An alternative is the sprite sheet, see https://www.gamefromscratch.com/post/2015/10/26/SFML-CPP-Tutorial-Spritesheets-and-Animation.aspx I can re-write the answer with the sprite sheet, but I think you wanted to understand the basic mechanism of an animation first. Please indicate so in a comment to my post.
Btw, if you are not used to game loops, have a look on https://gafferongames.com/post/fix_your_timestep/
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow renderWindow(sf::VideoMode(800, 600), "Color Animation");
// Duration to control animation speed
int currentFrame = 1;
float duration = float();
sf::Clock clock;
sf::Event event;
sf::Texture arrayOfTexture[9];
arrayOfTexture[0].loadFromFile("1.png");
arrayOfTexture[1].loadFromFile("2.png");
arrayOfTexture[2].loadFromFile("3.png");
arrayOfTexture[3].loadFromFile("4.png");
arrayOfTexture[4].loadFromFile("5.png");
arrayOfTexture[5].loadFromFile("6.png");
arrayOfTexture[6].loadFromFile("7.png");
arrayOfTexture[7].loadFromFile("8.png");
arrayOfTexture[8].loadFromFile("9.png");
// Assign basic texture to sprite
sf::Sprite imageOfLamp;
imageOfLamp.setTexture(arrayOfTexture[0]);
while (renderWindow.isOpen()){
// How much time since last loop?
sf::Time dt = clock.restart();
duration += dt.asSeconds();
while (renderWindow.pollEvent(event)){
//Handle events here
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
// Animation duration per frame (0.1f) reached
if (duration > 0.1f){
// Restart calculation of the duration
duration = 0;
// Loop through the animation frames
if (currentFrame < 9){
currentFrame += 1;
} else {
// Start from first frame if last frame reached
currentFrame = 0;
}
imageOfLamp.setTexture(arrayOfTexture[currentFrame]);
}
// Clear render window and draw sprite
renderWindow.clear(sf::Color::Black);
renderWindow.draw(imageOfLamp);
renderWindow.display();
}
}

SFML 2 drawing does not work when connected with OGRE

I am currently working on connecting OGRE and SFML.
SFML should be used for 2D drawing, network stuff and input.
OGRE is for the 3d Graphics.
Currently the whole thing is on Linux.
What works: Connecting OGRE and SFML. First I create a SFML Render Window, then I grab the handle of this window and give it to the OGRE Render WIndow while creating it. I can use the SFML Events now. Did not test the Network stuff, but I am sure this will work too.
What does not work: Drawing in the SFML window.
Case 1: SFML and OGRE are not connected. OGRE does not have the SFML window handle and has its own window. SFML still can't draw in its own window! The main loop executes a maximum of 3 times and then just stops. Nothing more happens. A few seconds later (about 20 or so) I get a Memory Access violation and the program ends.
Case 2: SFML and OGRE are connected. A similar thing happens: The main loop executes exectly 53 times, nothing gets drawn and then the program stops with the terminal message "aborted" (actually its "Abgebrochen", because it's in German)
The strange behaviour also happens, when I let SFML draw into a sf::RenderTexture instead of the sfml_window.
Here is my code:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <OGRE/Ogre.h>
#include <vector>
#include <stdio.h>
int main(int argc, char * argv[])
{
if(argc == 1)
return -1;
// start with "1" and you get 1 window, start with "0" and you get two
bool together = atoi(argv[1]);
// create the SFML window
sf::RenderWindow sfml_window(sf::VideoMode(800, 600), "test");
sf::WindowHandle sfml_system_handle = sfml_window.getSystemHandle();
sfml_window.setVerticalSyncEnabled(true);
std::cout<<sfml_system_handle<<std::endl;
// init ogre
Ogre::Root * ogre_root = new Ogre::Root("", "", "");
std::vector<Ogre::String> plugins;
plugins.push_back("/usr/lib/x86_64-linux-gnu/OGRE-1.8.0/RenderSystem_GL");
for(auto p : plugins)
{
ogre_root->loadPlugin(p);
}
const Ogre::RenderSystemList& render_systems = ogre_root->getAvailableRenderers();
if(render_systems.size() == 0)
{
std::cerr<<"no rendersystem found"<<std::endl;
return -1;
}
Ogre::RenderSystem * render_system = render_systems[0];
ogre_root->setRenderSystem(render_system);
ogre_root->initialise( false, "", "");
// create the ogre window
Ogre::RenderWindow * ogre_window= NULL;
{
Ogre::NameValuePairList parameters;
parameters["FSAA"] = "0";
parameters["vsync"] = "true";
// if started with 1, connect the windows
if(together) parameters["externalWindowHandle"] = std::to_string(sfml_system_handle);
ogre_window = ogre_root->createRenderWindow("ogre window", 800, 600, false, &parameters);
}
// ogre stuff
Ogre::SceneManager * scene = ogre_root->createSceneManager(Ogre::ST_GENERIC, "Scene");
Ogre::SceneNode * root_node = scene->getRootSceneNode();
Ogre::Camera * cam = scene->createCamera("Cam");
Ogre::SceneNode * cam_node = root_node->createChildSceneNode("cam_node");
cam_node->attachObject(cam);
Ogre::Viewport * vp = ogre_window->addViewport(cam);
vp->setAutoUpdated(false);
vp->setBackgroundColour(Ogre::ColourValue(0, 1, 1));
ogre_window->setAutoUpdated(false);
ogre_root->clearEventTimes();
//sfml image loading
sf::Texture ring;
std::cout<<"ring loading: "<<ring.loadFromFile("ring.png")<<std::endl;
sf::Sprite sprite;
sprite.setTexture(ring);
// main loop
int counter = 0;
while(!ogre_window->isClosed() && sfml_window.isOpen())
{
std::cout<<counter<<std::endl;
counter++;
std::cout<<"a"<<std::endl;
// sfml events
sf::Event event;
while(sfml_window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
{
sfml_window.close();
}
}
std::cout<<"b"<<std::endl;
std::cout<<"c"<<std::endl;
ogre_root->renderOneFrame();
std::cout<<"d"<<std::endl;
// here the strange behaviour happens
// if this line (draw) isn't present, everything works
sfml_window.pushGLStates();
sfml_window.draw(sprite);
sfml_window.popGLStates();
vp->update();
std::cout<<"e"<<std::endl;
sfml_window.display();
// only needs to be done for separated windows
// sfml display updates otherwise, both use double buffering
if(!together) ogre_window->update(true);
}
return 0;
}
Help would be really appreciated.
EDIT: I added the pushGLStates(); and popGLStates(); commands, forgot those earlier!
Not an answer really, but too long for a comment:
ogre_window = ogre_root->createRenderWindow("ogre window", 800, 600, false, &parameters);
Are you sure that it's okay to pass the address of an object you destroy the very next line here with &parameters?

SFML 2.0 Targeting a player by a single turret

At the beginning - sorry for my english (i'm still learning).
I've created a turret which targets player. It works fine but when i'm moving around within the range of tower, turret no longer targets me. Just take a look at this code and run this in your compilator.
int detection (sf::Sprite statek,sf::RectangleShape linia,sf::Texture textstatku)
{
sf::FloatRect rect, rect2;
rect = linia.getGlobalBounds();
rect2 = statek.getGlobalBounds();
if(rect2.intersects(rect))
return 1;
else
return 2;
}
int _tmain(int argc, _TCHAR* argv[])
{
sf::Event evente;
sf::RenderWindow okno ( sf::VideoMode(500,500,32)," TURRET TEST ");
sf::Texture textturreta;
textturreta.loadFromFile ("C:\\Users\\Darono\\C++\\Projekty\\IN PROGGRES\\Single turret\\Debug\\turret.png");
sf::CircleShape turret (20.0,100);
turret.setTexture((sf::Texture *)&textturreta);
turret.setPosition (240,240);
sf::Texture Lufatext;
Lufatext.loadFromFile("C:\\Users\\Darono\\C++\\Projekty\\IN PROGGRES\\Single turret\\Debug\\Lufa.png");
sf::Sprite lufa;
lufa.setTexture(Lufatext);
sf::Texture gracztext;
gracztext.loadFromFile("C:\\Users\\Darono\\C++\\Projekty\\IN PROGGRES\\Single turret\\Debug\\gracz.png");
sf::Sprite gracz(gracztext);
int orginY=turret.getPosition().y+20;
int orginX=turret.getPosition().x+20;
lufa.setPosition(turret.getPosition().x+20,turret.getPosition().y+20);
lufa.setOrigin (2,-20);
sf::RectangleShape liniastrzalu(sf::Vector2f(1,200));
liniastrzalu.setOrigin(0,-20);
liniastrzalu.setPosition(turret.getPosition().x+20,turret.getPosition().y+20);
int a =0;
while (okno.isOpen())
{
if (gracz.getPosition().y >= turret.getPosition().y-240||gracz.getPosition().y <= turret.getPosition().y+280)
{
if (detection(gracz,liniastrzalu,textturreta)== 1)
{
std::cout <<"lol";
}
if (detection(gracz,liniastrzalu,textturreta)==2)
{
lufa.rotate(1);
liniastrzalu.rotate(1);
}
}
while (okno.pollEvent(evente))
{
//lufa obraca się razem z kołem
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
gracz.move(-2,0);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
gracz.move(2,0);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
gracz.move(0,2);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
gracz.move(0,-2);
}
}
okno.display();
okno.clear();
okno.draw(turret);
okno.draw(lufa);
okno.draw(gracz);
//okno.draw(liniastrzalu);
}
return 0;
}
You should follow a few best practices:
Your game loop consists of 3 main parts: handling user input, updating your game world and drawing your game world. Although you do this, you have the order mixed up. You seem to update your game world before handling user input.
Drawing in SFML consists of cleaning the surface, drawing and presenting in that order.
okno.clear();
okno.draw(turret);
okno.draw(lufa);
okno.draw(gracz);
okno.display();
Notice how I put the display call to the end.
You don't need to call your detection method twice. Call it once and store the result in a variable, then use that variable.
Fix those things first, because they will cause a lot of problems that may hide your real problem or cause problems when your code is fine.

SFML 2.0 Looping a sprite to display more than once

I have asked a similar question in the past but I still can't get my head around this. I am doing an invaders game based on SFML 2.0. So far I have one sprite sheet which runs through using my clock. This part works just fine:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <string>
int spriteWalkSpeed = 10;
int DownspriteWalkSpeed = 5;
int up=-spriteWalkSpeed, down=DownspriteWalkSpeed, left=-spriteWalkSpeed, right=spriteWalkSpeed;
int xVelocity =0, yVelocity=0;
const int SC_WIDTH=800;
const int SC_HEIGHT= 600;
const float REFRESH_RATE =0.01f; //how often we draw the frame in seconds
const double delay=0.1;
const int SPRITEROWS=1; //number of ROWS OF SPRITES
const int SPRITECOLS=2;//number of COLS OF SPRITES
std::string gameOver = "Game Over";
int main()
{
// Create the main window
sf::RenderWindow App (sf::VideoMode(SC_WIDTH, SC_HEIGHT, 32), "Space Invaders!",sf::Style::Close );
// Create a clock for measuring time elapsed
sf::Clock Clock;
//background texture
sf::Texture backGround;
backGround.loadFromFile("images/background.jpg");
sf::Sprite back;
back.setTexture(backGround);
//load the invaders images
sf::Texture invaderTexture;
invaderTexture.loadFromFile("images/invaders.png");
sf::Sprite invadersSprite(invaderTexture);
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
int invadersWidth=invaderTexture.getSize().x;
int invadersHeight=invaderTexture.getSize().y;
int spaceWidth=invadersWidth/SPRITECOLS;
int spaceheight=invadersHeight/SPRITEROWS;
//Sprites
sf::IntRect area(0,0,spaceWidth,spaceheight);
invadersSprite.setTextureRect(area);
invadersSprite.setPosition(30, NULL);
App.setKeyRepeatEnabled(false);
//Collision detection
// Start game loop
while (App.isOpen())
{
// Process events
sf::Event Event;
while (App.pollEvent(Event))
{
// Close window : exit
if (Event.type == sf::Event::Closed)
App.close();
}
// Create an array of 10 sprites (cannot initialise them with textures here)
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setPosition(30,0);
if(Clock.getElapsedTime().asSeconds()>REFRESH_RATE)
{
//carry out updating tasks
static float spriteTimer=0.0; //keep track of sprite time
spriteTimer+=Clock.getElapsedTime().asSeconds();
static int count=0; //keep track of where the sub rect is
if(spriteTimer>delay)
{
invaderSprites[i].setTextureRect(area);
++count;
invaderSprites[i].move(xVelocity, yVelocity);
if(count==SPRITECOLS) //WE HAVE MOVED OFF THE RIGHT OF THE IMAGE
{
area.left=0; //reset texture rect at left
count=0; //reset count
}
else
{
area.left+=spaceWidth; //move texture rect right
}
spriteTimer=0; //we have made one move in the sprite tile - start timing for the next move
}
Clock.restart();
}
App.draw(back);
App.draw(invaderSprites[i]);
// Finally, display the rendered frame on screen
App.display();
}
}
return EXIT_SUCCESS;
}
The issue I am having is that the sprite only shows once, not 10 times (as the for loop states)
std::vector<sf::Sprite> invaderSprites(10, sf::Sprite(invaderTexture));
// Loop over the elements of the vector of sprites
for (int i = 0; i < invaderSprites.size(); i++)
{
invaderSprites[i].setPosition(30, NULL);
}
// Create an array of 10 sprites (cannot initialise them with textures here)
sf::Sprite invaderSprites[10]; // Loop over each sprite, setting their textures
for (int i = 0; i < 10; i++)
{
invaderSprites[i].setTexture(invaderTexture);
}
I am pretty sure it has something to do with the app drawing invadersSprite whereas the loop is setup for invaderSprites. Even just a little insight into what is going wrong would be such a big help.
I am pretty sure it has something to do with the app drawing
invadersSprite whereas the loop is setup for invaderSprites.
Yes, that certainly has something to do with it. You need to call App.draw(...) for each sprite that you want to draw. You're not calling it for any of the sprites in your vector. For that, you would want a loop:
for (int i=0; i<invaderSprites.size(); ++i)
App.draw(invaderSprites[i]);
There are other problems though. For example, why are you declaring an array of sprites called invaderSprites, when you already have a vector of sprites with that same name? The latter hides the former once it is declared.
Another thing is that you are setting all the sprites to the same position, so even if if you do manage to draw them all, they will all be in the same spot, and as such they will not appear as separate objects.