making sfml c++ scroll bar won't draw shapes inside a veiw - c++

So i'm trying to make a basic gui with sfml and need a scroll bar to scroll through a loop of drawables. After research I learned that view manipulation would be the way to do it. I have a rectangle for an outline that i'm trying to use as the view and have set up the bar itself i just need it to change the view. My issue is that I isn't drawing anything I put inside the view. If it won't draw anything in the view its hard to scroll through it. All help is appreciated thanks.
my deliration
projectsBox = sf::RectangleShape(sf::Vector2f((400 * scale) - (2 * (10 * scale)), 225 * scale));
projectsBox.setOrigin(sf::Vector2f(-10 * scale, -130 * scale));
projectsBox.setOutlineColor(sf::Color::Black);
projectsBox.setOutlineThickness(10 * scale);
projectsBox.setFillColor(sf::Color::Transparent);
projectsVeiw.setViewport(projectsBox.getGlobalBounds());
void sfmlWindow::drawProjects() {
sf::CircleShape base;
base.setRadius(30);
base.setScale(0, userProjects->projects[1].name.length());
base.setFillColor(sf::Color::Blue/*sf::Color(33, 33, 33, 270)*/);
base.setOrigin(0, 0);
sf::Text text;
textInit(&text, userProjects->projects[1].name, 96);
text.setOrigin(0, 0);
projectsDrawables.emplace_back(std::make_unique<sf::CircleShape>(base));
projectsDrawables.emplace_back(std::make_unique<sf::Text>(text));
}
My Loop
while (mainWindow.isOpen()) {
eventLoop();
mainWindow.clear(sf::Color::Transparent);
//Do project veiw
mainWindow.setView(projectsVeiw);
drawProjects();
for (std::unique_ptr<sf::Drawable>& i : projectsDrawables) {
mainWindow.draw(*i);
}
//draw drawbles
mainWindow.setView(mainWindow.getDefaultView());
for (std::unique_ptr<sf::Drawable>& i : allDrawables) {
mainWindow.draw(*i);
}
mainWindow.draw(projectsBox);
projectsSlider.setSize(sf::Vector2f(15, slidersize()));
mainWindow.draw(projectsSlider);
//render
mainWindow.display();
}

I've adapted your code to something that compiles and actually draws stuff on the screen. It's not how you want it to look but it show drawing through both views working. You should be able to experiment with this to progress.
projectDrawables are located around 1000,1000 in your world where as allDrawables are positioned around 0,0. To shrink you projectBox to just be in the corner you should increase the size of the projectView.
#include <iostream>
#include <SFML/Graphics.hpp>
const int BOARD_SIZE = 40;
const float TILE_SIZE = 20.0f;
std::vector<std::unique_ptr<sf::Drawable> > projectsDrawables;
std::vector<std::unique_ptr<sf::Drawable> > allDrawables;
sf::Font font;
void drawProjects() {
sf::CircleShape base;
base.setRadius(30);
base.setFillColor(sf::Color::Blue);
base.setPosition(1000, 1000);
sf::Text text;
text.setFont(font);
text.setFillColor(sf::Color::White);
text.setString("hello");
text.setCharacterSize(96);
text.setOrigin(1100, 1000);
projectsDrawables.emplace_back(std::make_unique<sf::Text>(text));
projectsDrawables.emplace_back(std::make_unique<sf::CircleShape>(base));
sf::CircleShape base2;
base2.setRadius(50);
base2.setFillColor(sf::Color::Red);
base2.setPosition(30, 80);
sf::Text text2;
text2.setFont(font);
text2.setFillColor(sf::Color::Green);
text2.setString("Cheese");
text2.setCharacterSize(48);
text2.setPosition(30, 30);
allDrawables.emplace_back(std::make_unique<sf::Text>(text2));
allDrawables.emplace_back(std::make_unique<sf::CircleShape>(base2));
}
int main()
{
sf::RenderWindow mainWindow(sf::VideoMode((2+BOARD_SIZE) * (int)TILE_SIZE, (2+BOARD_SIZE) * (int)TILE_SIZE), "Snake");
if (!font.loadFromFile("Instruction.ttf") ) {
std::cerr << "Font error." << std::endl;
exit( -1 );
}
sf::Clock clock;
auto scale = 1;
auto projectsBox = sf::RectangleShape(sf::Vector2f(400, 225));
projectsBox.setPosition(1000,1000);
projectsBox.setOutlineColor(sf::Color::Green);
projectsBox.setOutlineThickness(10 * scale);
projectsBox.setFillColor(sf::Color::Transparent);
sf::View projectsView( projectsBox.getGlobalBounds());
while (mainWindow.isOpen()) {
sf::Time elapsed = clock.getElapsedTime();
if (elapsed.asSeconds() > 0.2f) {
clock.restart();
}
sf::Event event;
while (mainWindow.pollEvent(event)) {
if (event.type == sf::Event::Closed)
mainWindow.close();
// Respond to key pressed events
if (event.type == sf::Event::KeyPressed){
if (event.key.code == sf::Keyboard::Escape){
return 0;
}
}
}
mainWindow.clear(sf::Color::Black);
mainWindow.setView(projectsView);
mainWindow.draw(projectsBox);
//Do project veiw
drawProjects();
for (std::unique_ptr<sf::Drawable>& i : projectsDrawables) {
mainWindow.draw(*i);
}
//draw drawbles
mainWindow.setView(mainWindow.getDefaultView());
for (std::unique_ptr<sf::Drawable>& i : allDrawables) {
mainWindow.draw(*i);
}
//projectsSlider.setSize(sf::Vector2f(15, slidersize()));
//mainWindow.draw(projectsSlider);
//render
mainWindow.display();
}
return 0;
}

Related

button in cpp sfml lib don't respond to my mouse input

I have to make Quarto game as a GUI program so firstly I wanted to make buttons in menu, but I don't know why it don't fill color when my mouse is at the button.
Code:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include<bits/stdc++.h>
using namespace std;
using namespace sf;
class button{
public:
bool isClicked=false;
int Yposition;
int Xposition;
bool isMouseOn=false;
};
int main(){
//Defining variables
Vector2i mouseWindowPosition;
//rendering window "window"
RenderWindow window(VideoMode(800,600), "Quarto game - menu", Style::Default);
window.display();
//setting vsync
window.setVerticalSyncEnabled(true);
//loading textures
Texture backgroundTexture;
backgroundTexture.loadFromFile("Resources/background.png");
//making sprites
Sprite backgroundSprite;
backgroundSprite.setTexture(backgroundTexture);
backgroundSprite.setPosition(0,0);
//making buttons and their colors
RectangleShape playButton(Vector2f(200.f,50.f));
playButton.setFillColor(Color(128,128,128));
playButton.setOutlineThickness(5.f);
playButton.setOutlineColor(Color(100,100,100));
button play;
//setting position of buttons
play.Xposition=70;
play.Yposition=200;
//game loop
while(window.isOpen()){
Event event;
playButton.setFillColor(Color(128,128,128));
play.isMouseOn=false;
while(window.pollEvent(event)){
if(event.type==Event::Closed){
window.close();
}
}
//Getting mouse position
mouseWindowPosition=Mouse::getPosition(window);
if(mouseWindowPosition.x<=play.Xposition && mouseWindowPosition.y<=play.Yposition && mouseWindowPosition.x>=play.Xposition+200 && mouseWindowPosition.y>=play.Yposition+50){
play.isMouseOn=true;
}
//Drawing to screen
window.clear();
window.draw(backgroundSprite);
if(play.isClicked==false){
playButton.setPosition(Vector2f(play.Xposition, play.Yposition));
window.draw(playButton);
}
if(play.isMouseOn==true){
playButton.setFillColor(Color(128,128,128));
}
window.display();
}
}
Is there any better way to make buttons in sfml?
The immediate reason why the button doesn't fill is your if the statement has the signs reversed for checking the box. Using this if statement instead should work:
//Getting mouse position
mouseWindowPosition=Mouse::getPosition(window);
if (mouseWindowPosition.x>=play.Xposition && mouseWindowPosition.y>=play.Yposition &&
mouseWindowPosition.x<=play.Xposition+200 && mouseWindowPosition.y<=play.Yposition+50){
//your code here
}
If you are not already aware, the x and y values are arranged like a table where positive y goes down instead of a cartesian coordinate system where positive y is up.
The other problem is the color you are updating your fill with is the same color of the fill, so changing that color will get you your desired fill.
Also, logic wise you should swap the statement
if (play.isMouseOn == true)
{
}
with the statement
if (play.isClicked == false)
{
playButton.setPosition(Vector2f(play.Xposition, play.Yposition));
window.draw(playButton);
window.display();
}
Here is a working code that changes the fill from grey to red when you hover above it:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
using namespace std;
using namespace sf;
class button
{
public:
bool isClicked = false;
int Yposition;
int Xposition;
bool isMouseOn = false;
};
int main()
{
//Defining variables
Vector2i mouseWindowPosition;
//rendering window "window"
RenderWindow window(VideoMode(800, 600), "Quarto game - menu", Style::Default);
window.display();
//setting vsync
window.setVerticalSyncEnabled(true);
//loading textures
Texture backgroundTexture;
backgroundTexture.loadFromFile("Resources/background.png");
//making sprites
Sprite backgroundSprite;
backgroundSprite.setTexture(backgroundTexture);
backgroundSprite.setPosition(0, 0);
//making buttons and their colors
RectangleShape playButton(Vector2f(200.f, 50.f));
playButton.setFillColor(Color(100, 100, 100));
playButton.setOutlineThickness(5.f);
playButton.setOutlineColor(Color(100, 100, 100));
button play;
//setting position of buttons
play.Xposition = 70;
play.Yposition = 200;
//game loop
while (window.isOpen())
{
Event event;
playButton.setFillColor(Color(128, 128, 128));
play.isMouseOn = false;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
{
window.close();
}
}
//Getting mouse position
mouseWindowPosition = Mouse::getPosition(window);
if (mouseWindowPosition.x >= play.Xposition && mouseWindowPosition.y >= play.Yposition && mouseWindowPosition.x <= play.Xposition + 200 && mouseWindowPosition.y <= play.Yposition + 50)
{
play.isMouseOn = true;
}
//Drawing to screen
window.clear();
window.draw(backgroundSprite);
if (play.isMouseOn == true)
{
playButton.setFillColor(Color(128, 0, 0));
}
if (play.isClicked == false)
{
playButton.setPosition(Vector2f(play.Xposition, play.Yposition));
window.draw(playButton);
window.display();
}
}
}

Possible to add multiple bounding boxes to one sprite and resize them in SFML?

I'm making a Pacman game using SFML. I have a background image for the maze that I'm using and I was wondering how I would be able to use multiple bounding boxes for the background so that when the yellow Pacman character collides with the walls, it stops him. Also, I was wondering if I could use multiple bounding boxes for the background maze image, if so, how?
My code:
#include <SFML/Graphics.hpp>
#include <iostream>
int main(int argc, char** argv)
{
sf::RenderWindow renderWindow(sf::VideoMode(280, 480), "Pacman" /*, sf::Style::Titlebar | sf::Style::Close*/);
sf::Event event;
sf::Texture texture, texture2;
texture.setSmooth (true);
texture2.setSmooth (true);
texture.loadFromFile("images/pacmanbkgd.png");
sf::Sprite background_sprite(texture);
background_sprite.setScale(sf::Vector2f(1.25, 1.25));
texture2.loadFromFile("images/transparentPacman3Sprites.png");
sf::IntRect rectSourceSprite(0, 0, 18, 16);
sf::Sprite pacmanMC_sprite(texture2, rectSourceSprite); // sf::intrect(int rectleft, int recttop, int rectwidth, int rectheight)
// pacmanMC_sprite.setScale(sf::Vector2f(1, 1));
pacmanMC_sprite.move(sf::Vector2f(200, 345)); // set sprite at inital location point (140, 145)
sf::Clock clock;
//sets the origin of the sprite
sf::Rect<float> size = pacmanMC_sprite.getGlobalBounds();
pacmanMC_sprite.setOrigin(sf::Vector2f(size.width / 2, size.height / 2));
while (renderWindow.isOpen())
{
while (renderWindow.pollEvent(event))
{
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
if (clock.getElapsedTime().asSeconds() > 0.75f) // 0.07, used for making the mouth faster
{
if (rectSourceSprite.left == 34)
rectSourceSprite.left = 0;
else
rectSourceSprite.left += 17; // pushes the sprite towards the left using x axis
pacmanMC_sprite.setTextureRect(rectSourceSprite);
clock.restart();
}
// pacmanMC_sprite movement
// sprite keeps on moving to a different position when a the correct key is pressed for its rotation
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up))
{
pacmanMC_sprite.move(0.0f, -0.015f);
pacmanMC_sprite.setRotation(-90.f);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down))
{
pacmanMC_sprite.move(0.0f, 0.015f);
pacmanMC_sprite.setRotation(90.f);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left))
{
pacmanMC_sprite.move(-0.015f, 0.0f);
pacmanMC_sprite.setRotation(-180.f);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right))
{
pacmanMC_sprite.move(0.015f, 0.0f);
pacmanMC_sprite.setRotation(0);
}
renderWindow.clear();
renderWindow.draw(background_sprite);
renderWindow.draw(pacmanMC_sprite);
renderWindow.display();
}
return 0;
}

SFML - Weird RenderTexture behaviour when resizing window

I am currently trying to recreate Chess in SFML. Generating the board normally works as intended but when I am resizing the window I get weird white borders.
Before Resize:
After Resize:
It looks like the view is not aligned with the window properly so I think my problem is not in the board generation but how I am handling resizes. I thought manually updating the window view would help but it only ensured that the squares don't get streched. The border issue remains however so now I am quite clueless as to how I could fix this problem.
Board.h:
#pragma once
#include <SFML/Graphics.hpp>
class Board
{
public:
void createBoard(sf::Vector2u windowSize);
void drawBoard(sf::RenderWindow& window) const;
private:
sf::RenderTexture board;
sf::Color lightColor = sf::Color(159, 144, 176);
sf::Color darkColor = sf::Color(125, 74, 141);
sf::Color backColor = sf::Color(32, 31, 32);
};
Board.cpp:
#include <SFML/Graphics.hpp>
#include "Board.h"
void Board::createBoard(sf::Vector2u windowSize)
{
const float xOffset = static_cast<float>(windowSize.x - windowSize.y) / 2.f;
const float squareSize = static_cast<float>(windowSize.y) / 8.f;
board.create(windowSize.x, windowSize.y);
board.clear(backColor);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
sf::RectangleShape currSquare({ squareSize, squareSize });
currSquare.setFillColor((i + j) % 2 ? lightColor : darkColor);
currSquare.setPosition(xOffset + static_cast<float>(i) * squareSize, (static_cast<float>(j) * squareSize));
board.draw(currSquare);
}
}
}
void Board::drawBoard(sf::RenderWindow& window) const
{
window.draw(sf::Sprite(board.getTexture()));
}
main.cpp:
#include <SFML/Graphics.hpp>
#include "Board.h"
int main()
{
sf::RenderWindow window(sf::VideoMode(500, 300), "Chess");
sf::Event event;
sf::View view = window.getDefaultView();
Board board;
board.createBoard(window.getSize());
while (window.isOpen()) {
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
else if (event.type == sf::Event::Resized) {
view.setSize({ static_cast<float>(event.size.width), static_cast<float>(event.size.height) });
window.setView(view);
board.createBoard({ event.size.width, event.size.height });
}
}
window.clear(sf::Color::White);
board.drawBoard(window);
window.display();
}
return 0;
}
Does anyone know how I could fix this problem?
The problem is that you don't only need to resize the view, but also recenter it. As right now you are not doing it, the center remains where the smaller board was and the bigger view takes a chunk from the outside in the top left corner.
So just change your code like this:
...
else if (event.type == sf::Event::Resized) {
float w = static_cast<float>(event.size.width);
float h = static_cast<float>(event.size.height);
view.setSize({w , h});
view.setCenter({w/2.f , h/2.f}); // <----- !
window.setView(view);
board.createBoard({ event.size.width, event.size.height });
}
...

I don't see the animation of the sprite

This is code after includes:
Sprite player;
Texture playerTexture;
IntRect playerContainer(0, 0, 32, 32);
Vector2f playerPosition;
int playerDirection = 0; // 0 - fwd, 1 - back, 2 - stay
This is animation update method:
void updateAnims(Clock clock, float time) {
if(time > 0.3f) {
if(playerDirection == 0) playerContainer.top = 0;
else if(playerDirection == 1) playerContainer.top = 32;
else if(playerDirection == 2) playerContainer.top = 64;
if(playerContainer.left == 96) playerContainer.left = 0;
else playerContainer.left += 32;
player.setTextureRect(playerContainer);
clock.restart();
}
}
and this method is updated in "int main()" method.
int main() {
// Init window
RenderWindow window(VideoMode(800, 600), "RPG");
Clock gameClock;
Clock animClock;
float gameTime;
float animTime;
// Setting up the player
playerTexture.loadFromFile("player.png");
player.setTexture(playerTexture);
player.setTextureRect(playerContainer);
player.setScale(Vector2f(3.f, 3.f));
playerPosition.x = 30;
playerPosition.y = 120;
player.setPosition(playerPosition);
while(window.isOpen()) {
Event event;
while(window.pollEvent(event)) {
if(event.type == Event::Closed) {
window.close();
}
}
checkInputs(gameTime);
animTime = animClock.getElapsedTime().asSeconds();
updateAnims(animClock, animTime);
window.clear();
gameTime = gameClock.getElapsedTime().asMilliseconds();
gameClock.restart();
window.display();
}
return 0;
}
Turns out that sprite is created in main method, gets texture and texture shape, gets position, but not drawn. Why?
I think think the problem is in animation method but I tried different variations of solutions.
You should call window.draw(player) method between window.clear() and window.display() to actually draw player sprite on screen.

How do you flip texture in VertexArray?

I have a spritesheet animation drawn using a vertexarray. As an example let's say I have an arrow pointing to the right with a simple animation. I would like to be able to flip the texture on the x axis so that the arrow can also point left without the need to double the size of my spritesheet.
I know that this is possible with sprites by using a negative scale factor, but in cases where I have to use vertexarray I would like to know how this is possible. I tried giving the texcoords a negative width but it did not work. Here's my example code that isolates the issue as much as possible:
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
// CREATE THE WINDOW
sf::RenderWindow window(sf::VideoMode(200, 200), "Arrow");
window.setFramerateLimit(20);
// CREATE GRAPHICS
sf::Texture texArrow;
texArrow.loadFromFile("Arrow.png");
sf::VertexArray vaArrow;
sf::RenderStates rsArrow;
rsArrow.texture = &texArrow;
vaArrow.setPrimitiveType(sf::Quads);
vaArrow.resize(4);
vaArrow[0].position = sf::Vector2f(0, 0);
vaArrow[1].position = sf::Vector2f(100, 0);
vaArrow[2].position = sf::Vector2f(100, 100);
vaArrow[3].position = sf::Vector2f(0, 100);
vaArrow[0].texCoords = sf::Vector2f(0, 0);
vaArrow[1].texCoords = sf::Vector2f(100, 0);
vaArrow[2].texCoords = sf::Vector2f(100, 100);
vaArrow[3].texCoords = sf::Vector2f(0, 100);
// ARROW DIRECTION STATES
enum Directions { right, left};
Directions dir = right;
float AnimationFrame = 0;
// GAME LOOP
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed) { window.close(); }
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { dir = right; }
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { dir = left; }
}
// UPDATE
switch (dir)
{
case right: // NORMAL
std::cout << "Arrow is now pointing RIGHT" << std::endl;
vaArrow[0].texCoords = sf::Vector2f(AnimationFrame * 100 , 0);
vaArrow[1].texCoords = sf::Vector2f(AnimationFrame * 100 + 100 , 0);
vaArrow[2].texCoords = sf::Vector2f(AnimationFrame * 100 + 100 , 100);
vaArrow[3].texCoords = sf::Vector2f(AnimationFrame * 100 , 100);
break;
case left: // FLIPPED
std::cout << "Arrow is now pointing LEFT" << std::endl;
// THIS IS WHAT I HAVE TRIED BUT IT DOESN'T WORK:
vaArrow[0].texCoords = sf::Vector2f((AnimationFrame * 100) * -1 , 0);
vaArrow[1].texCoords = sf::Vector2f((AnimationFrame * 100 + 100) * -1 , 0);
vaArrow[2].texCoords = sf::Vector2f((AnimationFrame * 100 + 100) * -1 , 100);
vaArrow[3].texCoords = sf::Vector2f((AnimationFrame * 100) * -1 , 100);
break;
}
AnimationFrame++;
if (AnimationFrame == 5) { AnimationFrame = 0; } // Always loop 0 1 2 3 4
// RENDER
window.clear();
window.draw(vaArrow, rsArrow);
window.display();
}
return 0;
}
Spritesheet for the example:
There are many days to solve this, but your actual idea isn't that far off. Did you notice that you're basically stretching one end of the arrow over your whole quad?
The solution is pretty simple: By default. Textures aren't repeated, they're clamped (meaning that the pixels on the edges are repeated infinitely), which causes the glitch you've noticed.
So all you have to do to get your approach working is to set the texture to be repeated:
texArrow.setRepeated(true);