I draw the application menu, having previously written the menu class and the function of this class.
When I launch the application, the menu works for 5-7 seconds, but in Windows the cursor are spinning in the form of a blue circle, which does not bode well. If these 5-7 seconds are idle, the whole screen
turns white, and Windows says that the application is not responding. In this case, if you press where the buttons were broken, these buttons are triggered, the menu closes and the game starts. The problem lies in the description of the menu class, I think.
Code of Menu class:
class Menu
{
public:
Image menuImage1, menuImage2, menuImage3;
Texture menuTexture1, menuTexture2, menuTexture3;
int menuNum = 0;
bool isMenu;
list <Asteroid*> asteroidsMass;
list <Asteroid*> ::iterator it;
Sprite menu1, menu2, menu3;
Menu()
{
menuImage1.createMaskFromColor(Color::Black);
menuImage2.createMaskFromColor(Color::Black);
menuImage3.createMaskFromColor(Color::Black);
menuImage1.loadFromFile("title.jpg");
menuImage2.loadFromFile("start.jpg");
menuImage3.loadFromFile("exit.jpg");
menuTexture1.loadFromImage(menuImage1);
menuTexture2.loadFromImage(menuImage2);
menuTexture3.loadFromImage(menuImage3);
menu1.setTexture(menuTexture1);
menu2.setTexture(menuTexture2);
menu3.setTexture(menuTexture3);
menu1.setPosition(scrX / 2, 200);
menu2.setPosition((scrX / 2) - 172.5, 300);
menu3.setPosition((scrX / 2) - 140, 450);
menu1.setOrigin(300, 75);
for (int i = 0; i < 15; i++)
{
Asteroid* aster = new Asteroid(rand() % 1200, rand() % 800, 5, 25, 0);
asteroidsMass.push_back(aster);
}
isMenu = true;
}
void update(RenderWindow& app)
{
while (isMenu)
{
menu2.setColor(Color::White);
menu3.setColor(Color::White);
app.clear(Color::Black);
menuNum = 0;
if (IntRect((scrX / 2) - 172, 300, 360, 100).contains(Mouse::getPosition(app))) { menu2.setColor(Color::Green); menuNum = 2; }
else if (IntRect((scrX / 2) - 172, 450, 360, 100).contains(Mouse::getPosition(app))) { menu3.setColor(Color::Green);menuNum = 3; }
if (Mouse::isButtonPressed(Mouse::Left))
{
if (menuNum == 2)
{
for (it = asteroidsMass.begin(); it != asteroidsMass.end(); it++)
{
Asteroid* asteroid = *it;
it = asteroidsMass.erase(it);
delete asteroid;
}
isMenu = false;
}
if (menuNum == 3)
{
for (it = asteroidsMass.begin(); it != asteroidsMass.end(); it++)
{
Asteroid* asteroid = *it;
it = asteroidsMass.erase(it);
delete asteroid;
}
isMenu = false;
app.close();
}
}
for (it = asteroidsMass.begin(); it != asteroidsMass.end(); it++)
{
app.draw((*it)->EntityShape);
}
for (it = asteroidsMass.begin(); it != asteroidsMass.end(); it++)
{
(*it)->update();
}
app.draw(menu1);
app.draw(menu2);
app.draw(menu3);
app.display();
}
}
};
All code:
https://pastebin.pl/view/0c1d6c9e
I SOLVED MY PROBLEM!
If you have same problem, just write in "while" loop checking of closing window:
while(isMenu)
{
Event event;
while (app.pollEvent(event))
{
if (event.type == Event::Closed)
app.close();
}
//some code
}
Related
I have a minor problem in my code and can't resolve it.
I created a Button class using SFML library. Its just code practice since I'm relatively new to programing.
But the problem is that I can't make it work on release, it only works when pressed. So instead of one click it registers multiple clicks while it is being pressed. It's best to try the given code to see what I mean.
Thanks in advance!
Here is code for Button class:
class Button
{
private:
//Button Text Params:
sf::Font font;
sf::Text text;
sf::Vector2f textPos;
sf::Vector2f textBounds;
//Button Shape Params:
sf::RectangleShape shape;
sf::Vector2f pos;
sf::Vector2f size;
//Button colors(Hover and Default)
sf::Color DefaultColor;
sf::Color hColor;
//Button Sounds:
sf::SoundBuffer bf1;
sf::SoundBuffer bf2;
sf::Sound HoverSound;
sf::Sound ClickSound;
bool wFocus = false; //Returns focus on main window
bool ButtonClick = false; //Controls the code on button click
bool CursorOverButton(sf::RenderWindow &window) //Detect cursor pos on window
{
sf::Vector2f CursorPos = (sf::Vector2f)sf::Mouse::getPosition(window);
if((CursorPos.x < (pos.x + size.x) && CursorPos.x > pos.x) && (CursorPos.y < (pos.y + size.y) && CursorPos.y > pos.y))
{
if(shape.getFillColor() != hColor){ //if cursor passes over window
shape.setFillColor(hColor); //all of this will execute
HoverSound.play();
}
return true;
}
else
{
shape.setFillColor(DefaultColor);
return false;
}
}
public:
Button()
{
font.loadFromFile("arial.ttf"); //Loads font for Button text default is ariel(change to what you want!)
}
//Button Creation:
void create(std::string ButtonText, int TextSize, sf::Color Color, float buttonWidth, float buttonHeight, float x, float y, sf::Color HoverColor)
{
//Create Button size and position:
pos.x = x;
pos.y = y;
size.x = buttonWidth;
size.y = buttonHeight;
DefaultColor = Color;
hColor = HoverColor;
//Load text for button:
text.setFont(font);
text.setCharacterSize(TextSize);
text.setString(ButtonText);
text.setOutlineColor(sf::Color::Black);
text.setOutlineThickness(2);
sf::FloatRect fr = text.getGlobalBounds();
textBounds.x = fr.width + fr.left;
textBounds.y = fr.height + fr.top;
//Apply parameters to shape:
shape.setSize(size);
shape.setFillColor(DefaultColor);
shape.setPosition(pos);
//Fit text to button:
text.setOrigin(textBounds.x/2, textBounds.y/2);
text.setPosition(pos.x + (size.x/2), pos.y + (size.y/2) - 1);
}
bool isButtonClicked(sf::RenderWindow &window) //When button is pressed down whis executes
{
if(!CursorOverButton(window)) //Tests focus on window
{
wFocus = (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))? true : false;
}
if(CursorOverButton(window) && !wFocus && sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))
{
if(!ButtonClick) ClickSound.play(); //Plays only on first click
ButtonClick = true;
return true;
}
else ButtonClick = false; //Return clicked state
return false;
}
void HoverClickSound(std::string HoverSoundPath1, std::string ClickSoundPath2) //add sounds to buffer and load into
{ //playable sound
bf1.loadFromFile(HoverSoundPath1);
HoverSound.setBuffer(bf1);
bf2.loadFromFile(ClickSoundPath2);
ClickSound.setBuffer(bf2);
}
void draw(sf::RenderWindow &window) //draws button on window(with text)
{
window.draw(shape);
if(textBounds.x < size.x || textBounds.y < size.y) window.draw(text);
}
};
Full Code with test is here on github
So I came across a solution(for the most part) it still registers on being pressed but registers only the first initial press down, once, and doesn't execute the wanted code more then once. Say you want to increment a value by one when button is clicked it used to run the code while button is pressed multiple times(because of the refresh rate), now this part of code wont let it continue after initial press down:
bool isButtonClicked(sf::RenderWindow &window) //When button is pressed down whis executes
{
if(!CursorOverButton(window)) //Tests focus on window
{
wFocus = (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))? true : false;
}
if(CursorOverButton(window) && !wFocus && sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))
{
if(!ButtonClick) //This wont let it execute more then once on click
{
ClickSound.play();
ButtonClick = true;
return true;
}
else return false;
}
else ButtonClick = false; //Return clicked state
return false;
}
Thoughts?
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;
}
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.
I added xdg-shell v5 to app to draw windows. When I send set_fullscreen or set_maximize commands, I see correctly set window states and correct sizes in configure event but nothing is happens.
My configure event function:
static void
xdg_surface_handle_configure(void *data,
struct xdg_surface *xdg_surface,
int32_t width,
int32_t height,
struct wl_array *states,
uint32_t serial) {
printf("Configure event got, width: %d, height: %d\n", width, height);
VirtIOGPU *g = (VirtIOGPU*) data;
g->window_state.activated = g->window_state.fullscreen =
g->window_state.maximized = g->window_state.resizing = false;
uint32_t *state;
wl_array_for_each(state, states) {
if(*state == XDG_SURFACE_STATE_MAXIMIZED) {
printf("Surface state: XDG_SURFACE_STATE_MAXIMIZED\n");
g->window_state.maximized = true;
} else if(*state == XDG_SURFACE_STATE_FULLSCREEN) {
printf("Surface state: XDG_SURFACE_STATE_FULLSCREEN\n");
g->window_state.fullscreen = true;
} else if(*state == XDG_SURFACE_STATE_RESIZING) {
printf("Surface state: XDG_SURFACE_STATE_RESIZING\n");
g->window_state.resizing = true;
} else if(*state == XDG_SURFACE_STATE_ACTIVATED) {
printf("Surface state: XDG_SURFACE_STATE_ACTIVATED\n");
g->window_state.activated = true;
}
}
if (width > 0 && height > 0) {
g->prev_width = g->width;
g->prev_height = g->height;
g->width = width;
g->height = height;
} else {
g->width = g->prev_width;
g->height = g->prev_height;
}
xdg_surface_ack_configure(xdg_surface, serial);
wl_surface_damage(g->surface, 0, 0, g->width, g->height);
wl_surface_commit(g->surface);
wl_display_dispatch_pending(g->display);
wl_display_flush(g->display);
}
So, how to see maximized window after I sent set_maximized?
Is it possible to unminimize minimized window programmatically (now by Super+Tab)?
Your client probably needs to attach and commit a new buffer with the correct dimensions to the surface. The compositor has done everything it's supposed to do, and expects your client to resize.
Currently I have this player.cpp class that I'm using for my sprite animations. I'm using a counter to update every frame. It animates, but it flies through the animations.
I want to slow this down. I found code that can be used to slow down sprite animations but I'm unsure how to implement it into my current program.
Below are my player.cpp file and following it is the code I found that can slow down sprite animations. When I've tried to add a clock to the counterWalking++ it didn't animate at all, and I've tried implementing this code to the same effect.
player::player()
{
rect.setSize(sf::Vector2f(32, 32));
rect.setFillColor(sf::Color::White);
rect.setPosition(300, 300);
sprite.setTextureRect(sf::IntRect(0, 0, 32, 32));
}
void player::update()
{
sprite.setPosition(rect.getPosition());
}
void player::updateMovement()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
if (canMoveRight == true) {
rect.move(movementSpeed, 0);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 64, 32, 32));
direction = 4;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
if (canMoveLeft == true) {
rect.move(-movementSpeed, 0);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 32, 32, 32));
direction = 3;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
if (canMoveUp == true) {
rect.move(0, -movementSpeed);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 96, 32, 32));
direction = 1;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
if (canMoveDown == true) {
rect.move(0, movementSpeed);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 0, 32, 32));
direction = 2;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else {
//Player not moving
}
counterWalking++;
if (counterWalking == 3)
counterWalking = 0;
}
Here is the code I found that displays a slow animation:
int main()
{
sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");
sf::Event event;
sf::Texture texture;
texture.loadFromFile("images/player.png");
sf::IntRect rectSourceSprite(0, 0, 32, 32);
sf::Sprite sprite(texture, rectSourceSprite);
sf::Clock clock;
while (renderWindow.isOpen()) {
while (renderWindow.pollEvent(event)) {
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
if (clock.getElapsedTime().asSeconds() > 1.0f) {
if (rectSourceSprite.left == 96)
rectSourceSprite.left = 0;
else
rectSourceSprite.left += 32;
sprite.setTextureRect(rectSourceSprite);
clock.restart();
}
renderWindow.clear();
renderWindow.draw(sprite);
renderWindow.display();
}
}
Since a solution has been given in comment but not implemented in an answer, here's some kind of "code review"
Key Binding
This code is actually using the sf::Keyboard::isKeyPressed and sf::Keyboard::isKeyReleased wich needs to be called every loop while a simple boolean switched when the key is pressed/released could to the work using events.
while (renderWindow.pollEvent(event)) {
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
The Event used in the main loop does also contain information about KeyEvents but isn't used that way. I'll suggest sending the evet to the player eac time a key Event happens:
while (renderWindow.pollEvent(event)) {
switch(event.type){
case sf::Event::EventType::Closed:
renderWindow.close();
break;
case sf::Event::KeyPressed: case sf::Event::KeyReleased:
myPlayer.keyEvent(event);
break;
}
}
The keyEvent function in player should then look like this:
void player::keyEvent(sf::Event event){
//Keycode of your keyboard's arrows goes from 71 to 74
if (event.key.code >= 71 && event.key.code <= 74){
//Gets the array ID from the enumeration value
int ID = event.key.code - 71;
//Stores false if the key is release, and true if it's pressed
keys[ID] = (event.type == sf::Event::KeyPressed);
}
}
Wich changes each values of the array in Player.h:
private:
bool keys[4];
Movement
Now all you have to do is call the player update() function in your game loop each second:
if (clock.getElapsedTime().asSeconds() > 1.0f) {
if (rectSourceSprite.left == 96)
rectSourceSprite.left = 0;
else
rectSourceSprite.left += 32;
sprite.setTextureRect(rectSourceSprite);
myPlayer.update();
clock.restart();
}
In the update() function, ou will then build you movement vector based on wich key is pressed:
void player::update()
{
sf::Vector2f movementVector(0,0);
//Left
if (keys[0]){
movementVector.x -= movementSpeed;
//Sends the sprite Rectangle position based on the movement type
move(32);
}
//Right
if (keys[1]){
movementVector.x += movementSpeed;
move(64);
}
//Up
if (keys[2]){
movementVector.y -= movementSpeed;
move(96);
}
//Down
if (keys[3]){
movementVector.y += movementSpeed;
move(0);
}
}
The move function will then move your sprite Position depending on the movement vector and the counter, the sprite will then be set.
Your movement counter will be incremented up to 30 and your player may only move when this counter is equal to 0, 10 or 20:
void move(int spritePos){
//Here, the check() function should tell if the player isn't going outside of the screen / in a wall
if (check(sprite.getPosition() + movementVector)){
sprite.move(movementVector);
++counterWalking %= 30;
if(!counterWalking % 10)
sprite.setTextureRect(sf::IntRect(counterWalking / 10 * 32, spritePos, 32, 32));
}
}
There are multiple ways of doing this, that's only the way I would do it
Sounds like you might need to learn about controlling the time-step. Essentially you only execute certain code when a desired amount of time has passed.
You'll commonly see this site referenced so maybe check it out:
https://gafferongames.com/post/fix_your_timestep/