SFML 2.0 Targeting a player by a single turret - c++

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.

Related

Generating multiple amounts of the same sprite broken

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));

SFML not displaying sprite

I recently started working with sfml and I cannot solve this problem. I have two classes which should work and display my sprite but nothing shows on the screen. I have tried a few things but none of them have worked so far, that's why I've decided to ask here :/
Thanks for any of your help, tips will also be appreciated ;)
Main.cpp:
#include <SFML\Graphics.hpp>
#include "Player.hpp"
sf::RenderWindow frame;
sf::Texture player_texture;
Player player(player_texture, 100, 100);
bool quit;
bool handle_events() {
sf::Event event;
if (frame.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
return true;
}
}
return false;
}
void update() {
}
void render() {
frame.clear(sf::Color(127, 142, 123));
player.draw_player(frame);
frame.display();
}
void run() {
while (quit != true) {
quit = handle_events();
update();
render();
}
}
int main() {
player_texture.loadFromFile("player.png");
frame.create(sf::VideoMode(800, 600), "frame");
run();
return 0;
}
Player.cpp:
#include "Player.hpp"
Player::Player(sf::Texture & player_texture, int pos_x, int pos_y) {
player_sprite.setTexture(player_texture);
player_sprite.setPosition(pos_x, pos_y);
player_sprite.scale(4, 4);
}
void Player::draw_player(sf::RenderWindow & frame) {
frame.draw(player_sprite);
}
Player.hpp:
#ifndef Player_hpp
#define Player_hpp
#include <SFML\Graphics.hpp>
#include <iostream>
class Player
{
private:
sf::Sprite player_sprite;
public:
Player::Player(sf::Texture & player_texture, int pos_x, int pos_y);
void Player::draw_player(sf::RenderWindow & frame);
};
#endif
Your player sprite is initialized with an empty texture. Then you load another picture into the texture and the player sprite does not know this. All the calculations the sprite made on the texture (for example size) are now invalid.
Make sure you load the texture before you pass it to the sprite. And don't change it afterwards.
nvoigt is completely right.
You set the the texture of the sprite first (in the player constructor) and load it afterwards (in the main function):
...
player_sprite.setTexture(player_texture);
...
player_texture.loadFromFile("player.png");
...
You either have to reload the texture again with .setTexture inside the main function after the loading. Or you have to complete restructure you code.
By the way, this if (frame.pollEvent(event)) is not a good idea.
There could have been multiple events triggered in on frame, for example mouse movement and window close after that. With the if you would only handle the first event in this frame, which is the mouse movement, even if you were looking for the second event. So you should do it with a while (while (frame.pollEvent(event))) to make sure that all the events are being handled.
Are you sure the texture is loaded correctly? Check the return value of sf::Texture::loadFromFile to test.
More over, why your class Player does not extends from sf::Sprite? I think inheritance would be more appropriated that composition in this case.
Also, in your function handle_events, you can directly call the method RenderWindow::close when the user wants to close the window. Then, in your function run, call RenderWindow::isOpen to check if your app can continue. It's would be less dirty than this ugly not initialised quit variable.

sfml animation of explosion after mouseclick

I need your piece of advice. I'm using SFML and I need to play animation from the spritesheet(f.e. 64 frames and 40px width/height of each frame) after mouseclick event. The only solution I've come to is:
if (event.type == sf::Event::MouseButtonPressed) {
if (event.key.code == sf::Mouse::Left) {
float frame = 0;
float frameCount = 64;
float animSpeed = 0.005;
while (frame < frameCount) {
spriteAnimation->setTextureRect(sf::IntRect(int(frame)*w, 0, w, w));
frame += animSpeed;
window->draw(rect); // clear the area of displaying animation
window->draw(*spriteAnimation);
window->display();
}
...
But calling window->display() so many times is really not good;
Can you suggest better variants?
Instead of jamming all of your code for the animation into the event block you should spread it out.
Your design here is very inflexible in that if you ever want to display anything other than an animation you are going to have to call window->display() again outside of your event loop.
Generally in SFML your game loop proceeds similarly to as follows:
initialize();
while(running)
{
checkEvents();
clear();
update();
display();
}
Instead of performing all of the calculations and displaying for your animation inside the event's if statement you should set a bool or call a doAnimation() function of some sort. I've written a rough example below:
bool doAnimation = 0;
//declare frame, framespeed, etc
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.key.code == sf::Mouse::Left)
{
doAnimation = true;
//reset frame, framespeed, etc
}
}
clear();
if(doAnimation)
{
sprite->setTexture(...);
if(frame == endFrame)
{
doAnimation = 0;
}
drawSprite();
}
window->display();
There are tons of ways to solve your problem. My example is less flexible than I would think is ideal but depending on the needs of your program it may work fine. If you wanted to take the next step moving the animation into a class of some sort would make your life a lot easier in the long run.

Using window.draw() outside of main SFML

I am using SFML and this is the first time I have really used a library but I have a decent knowledge in C++. How do I access my window functions outside of main? e.g.
void checkWin()
{
if (iFilled[0] == 1 && iFilled[1] == 1 && iFilled[2] == 1) {
RectangleShape line(Vector2f(150, 5));
line.setPosition(10, 450);
window.draw(line); //error window is inside of main()
}
}
int main()
{
RenderWindow window;
window.create(VideoMode(800, 600), "Red vs. Green Peppers", Style::Close);
//more code
return 0;
}
First, you need to check those tutorials from SFML's site: http://www.sfml-dev.org/tutorials/2.1/ .
If you have decent knowledge in C++ you could use pointers and reference parameters:
void checkWin( sf::RenderWindow &window) { ... }
Anyway, to show an image to the screen you need to do that in a while, because, how you wrote that, you will show that image only for 1 frame, or less, because you don't know if window is still open.

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.