-
I'm creating a clone of space invaders, which actually is an old project that I'm attempting once more. The Draw function can draw everything like the Enemy Space Ship, the Bullets they shoot, but not the Player Ship that he uses to play the game. The most frustrating thing is that there is no error while compiling or executing the program.
Here's the code
Draw
// Draw
window.clear();
// Draw Enemy
for (unsigned int i = 0; i < e.size(); i++) {
window.draw(e[i].sprite);
}
// Draw Enemy Bullet
for (unsigned int i = 0; i < e.size(); i++) {
if (e[i].b.bExist == true) {
window.draw(e[i].b.sprite);
}
}
// Draw Ship
window.draw(ship.sprite);
window.display();
Entities
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
#include <stdlib.h>
#include <time.h>
extern short WIDTH, HEIGHT;
struct Bullet {
sf::Sprite sprite;
bool bExist = false;
float speed = 5;
bool amEnemy;
Bullet(bool amAnEnemy) : amEnemy(amAnEnemy) {
sprite.setTexture(tbullet);
sprite.setScale(0.05f,0.05f);
if (amEnemy == true) {
sprite.rotate(90);
}
else sprite.rotate(270);
}
void handler() {
// Should I Exist? & Move By Gravity
switch (amEnemy)
{
case true:
if (sprite.getPosition().y > HEIGHT) {
bExist = false;
break;
}
if (bExist == true) {
sprite.move(0, speed);
}
break;
case false:
if (sprite.getPosition().y < 0) {
bExist = false;
break;
}
if (bExist == true) {
sprite.move(0, -speed);
}
break;
}
}
};
struct Enemy {
sf::Sprite sprite;
Bullet b = (Bullet)true;
Enemy(int nIter) {
sprite.setTexture(tenemy);
sprite.setScale(0.1f, 0.1f);
sprite.setPosition(WIDTH * nIter / 5.0f, 0.0f);
}
void Shoot() {
b.bExist = true;
b.sprite.setPosition(sprite.getPosition());
}
void handler() {
if(b.bExist == false){
int possNum = rand() % 500 + 1;
if (possNum == 1) {
Shoot();
}
}
}
}enemy = (Enemy)1;
struct Ship {
sf::Sprite sprite;
Bullet b = (Bullet)false;
Ship() {
sprite.setTexture(tship);
sprite.setScale(0.5,0.5);
sprite.setPosition(WIDTH / 2 - sprite.getGlobalBounds().width / 2, HEIGHT * 5 / 6);
std::cout << "Ship Builded\n";
}
};
Thanks in advance
-
Are you sure it's not drawn at the top of the screen?
Because 5 / 6 is equal to 0
-
Yes, I've also outputed the position and it says that is at y = 666.
However 5 / 6 != 0
The setPosition(WIDTH / 2, HEIGHT * 5 / 6) means to put the sprite at the 5 / 6 of the screen height, so in the lower part
-
Yes, I've also outputed the position and it says that is at y = 666.
However 5 / 6 != 0
The setPosition(WIDTH / 2, HEIGHT * 5 / 6) means to put the sprite at the 5 / 6 of the screen height, so in the lower part
It probably expected to be so, but because both operands are integer C++ will use integer operations.
#include <iostream>
int main() {
std::cout<<5 / 6<<'\n';
}
Output: 0
Of course in your code HEIGHT * 5 part is evaluate first, so it won't be 0.
-
I've changed the code converting all integers in floats but the problem is the same
Ship() {
sprite.setTexture(tship);
sprite.setScale(0.5f,0.5);
sprite.setPosition((float)WIDTH / 2.0f - (float)sprite.getGlobalBounds().width / 2.0f, HEIGHT * 5.0f / 6.0f);
std::cout << "Ship Builded\n";
}
-
In that case can you provide complete (minimal if possible) code for reproducing the behaviour?
Or maybe your project is on Github, Gitlab or something like that?
-
main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include <thread>
#include <vector>
#include <stdlib.h>
#include "Collision.hpp"
#include "TextureLoad.h"
#include "Entity.h"
#include "Threads.h"
short WIDTH = 600, HEIGHT = 800;
std::vector<Enemy> e;
Ship ship;
int main()
{
srand(time(NULL));
// Setup Game Window
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "Space Invaders");
window.setFramerateLimit(60);
// Load Textures
textureLoad();
// SetUp Level
for (int i = 0; i < 5; i++) {
e.push_back(enemy = (Enemy)i);
}
// Threads
// Enemy
std::thread th_enemy(EnemyThread);
// Game Loop
while (window.isOpen())
{
// Event Handler
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
// Bullet Handler
for (unsigned int i = 0; i < e.size(); i++) {
if (e[i].b.bExist == true) {
e[i].b.handler();
}
}
// Draw
window.clear();
// Draw Enemy
for (unsigned int i = 0; i < e.size(); i++) {
window.draw(e[i].sprite);
}
// Draw Enemy Bullet
for (unsigned int i = 0; i < e.size(); i++) {
if (e[i].b.bExist == true) {
window.draw(e[i].b.sprite);
}
}
// Draw Ship
window.draw(ship.sprite);
window.display();
}
th_enemy.join();
return 0;
}
TextureLoad.h
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
sf::Texture tship, tenemy, tbullet;
bool textureLoad() {
if (tship.loadFromFile("../Textures/ship.png")) {
std::cout << "Ship Texture Succesfully Loaded\n";
if (tenemy.loadFromFile("../Textures/enemy.png")) {
std::cout << "Enemy Texture Succesfully Loaded\n";
if (tbullet.loadFromFile("../textures/bullet.png")) {
std::cout << "Bullet Texture Succesfully Loaded\n";
return true;
}
else return false;
}
else return false;
}
else return false;
}
Threads.h
#pragma once
#include <chrono>
extern std::vector<Enemy> e;
void EnemyThread() {
auto startTimer = std::chrono::steady_clock::now();
while (1) {
auto endTimer = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(endTimer - startTimer).count() >= 16.67) {
for (unsigned int i = 0; i < e.size(); i++) {
e[i].handler();
}
startTimer = std::chrono::steady_clock::now();
}
}
}
Entity.h
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
#include <stdlib.h>
#include <time.h>
extern short WIDTH, HEIGHT;
struct Bullet {
sf::Sprite sprite;
bool bExist = false;
float speed = 5;
bool amEnemy;
Bullet(bool amAnEnemy) : amEnemy(amAnEnemy) {
sprite.setTexture(tbullet);
sprite.setScale(0.05f,0.05f);
if (amEnemy == true) {
sprite.rotate(90);
}
else sprite.rotate(270);
}
void handler() {
// Should I Exist? & Move By Gravity
switch (amEnemy)
{
case true:
if (sprite.getPosition().y > HEIGHT) {
bExist = false;
break;
}
if (bExist == true) {
sprite.move(0, speed);
}
break;
case false:
if (sprite.getPosition().y < 0) {
bExist = false;
break;
}
if (bExist == true) {
sprite.move(0, -speed);
}
break;
}
}
};
struct Enemy {
sf::Sprite sprite;
Bullet b = (Bullet)true;
Enemy(int nIter) {
sprite.setTexture(tenemy);
sprite.setScale(0.1f, 0.1f);
sprite.setPosition(WIDTH * nIter / 5.0f, 0.0f);
}
void Shoot() {
b.bExist = true;
b.sprite.setPosition(sprite.getPosition().x / 2 + b.sprite.getPosition().x / 2, sprite.getPosition().y);
}
void handler() {
if(b.bExist == false){
int possNum = rand() % 500 + 1;
if (possNum == 1) {
Shoot();
}
}
}
}enemy = (Enemy)1;
struct Ship {
sf::Sprite sprite;
Bullet b = (Bullet)false;
Ship() {
sprite.setTexture(tship);
sprite.setScale(0.5f,0.5);
sprite.setPosition((float)WIDTH / 2.0f - (float)sprite.getGlobalBounds().width / 2.0f, HEIGHT * 5.0f / 6.0f);
std::cout << "Ship Builded\n";
}
};
This is the whole code
-
Well now it's obvious - (btw, code still doesn't compile), your texture load after initialization of global variable.
For quick fix add right after loading e.g:
textureLoad();
ship = Ship();
But I strongly recommend refactor all code.
-
Thanks now it works but it's now creating 2 instances of Ship :(
I've fixed it removing the constructor and writing all the code in a method called setUp, but it's not how I want to do it. I want to only write once Ship ship like I did with the Enemy instances.
I'm wondering why I had to add that line (ship = Ship() Is it the same as ship.Ship() ?). I'm pretty new to c++ so I still have a lot to learn.
I can't explain why it didn't compile on your computer, while on mine goes flawlessly. I have visual studio community
-
I think I'm getting nuts. Maybe I just need a break :o
Before optimizing the code I wanted to implement some more features, like making the ship shoot. But when I press the button to trigger the Shoot() function the Bullet's sprite isn't drawn on the screen, even if the function is called. Here's some code:
Ship Class:
struct Ship {
sf::Sprite sprite;
Bullet b = (Bullet)false;
int speed = 5;
enum Side {LEFT, RIGHT};
// Keys
bool isLeftPressed = false;
bool isRightPressed = false;
void setUp() {
sprite.setTexture(tship);
sprite.setScale(0.5f,0.5);
sprite.setPosition((float)WIDTH / 2.0f - (float)sprite.getGlobalBounds().width / 2.0f, HEIGHT * 5.0f / 6.0f);
std::cout << "Ship Builded\n";
}
void move(Side side) {
switch (side)
{
case Ship::LEFT:
sprite.move(-speed, 0);
break;
case Ship::RIGHT:
sprite.move(speed, 0);
break;
default:
break;
}
}
bool keepShipInScreen() {
if (sprite.getPosition().x < 0) {
sprite.setPosition(0, sprite.getPosition().y);
return true;
}
else if (sprite.getPosition().x > WIDTH - sprite.getGlobalBounds().width) {
sprite.setPosition(WIDTH - sprite.getGlobalBounds().width, sprite.getPosition().y);
return true;
}
else return false;
}
void Shoot() {
b.bExist = true;
b.sprite.setPosition(sprite.getPosition().x + sprite.getGlobalBounds().width / 2 - b.sprite.getGlobalBounds().height / 2, sprite.getPosition().y);
}
void handler() {
if (keepShipInScreen() == false) {
if (isLeftPressed == true) {
move(LEFT);
}
if (isRightPressed == true) {
move(RIGHT);
}
}
b.handler();
}
}ship;
Player's Thread:
void ShipThread() {
// Thread Settings
auto ssTimer = std::chrono::steady_clock::now();
while (1) {
auto seTimer = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(seTimer - ssTimer).count() >= 16.67) { // Set Looprate to 60
// Thread
// Player Input
// Move Left
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
ship.isLeftPressed = true;
}
else ship.isLeftPressed = false;
// Move Right
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
ship.isRightPressed = true;
}
else ship.isRightPressed = false;
// Shoot
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
ship.Shoot();
}
// Player Handler
ship.handler();
ssTimer = std::chrono::steady_clock::now(); // Restart Timer
}
}
}
Texture Loading Function:
sf::Texture tship, tenemy, tbullet;
bool textureLoad() {
if (tship.loadFromFile("../Textures/ship.png")) {
std::cout << "Ship Texture Succesfully Loaded\n";
if (tenemy.loadFromFile("../Textures/enemy.png")) {
std::cout << "Enemy Texture Succesfully Loaded\n";
if (tbullet.loadFromFile("../textures/bullet.png")) {
std::cout << "Bullet Texture Succesfully Loaded\n";
return true;
}
else return false;
}
else return false;
}
else return false;
}
main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include <thread>
#include <vector>
#include <stdlib.h>
#include "TextureLoad.h"
#include "Entity.h"
#include "Threads.h"
short WIDTH = 600, HEIGHT = 800;
sf::RenderWindow window;
std::vector<Enemy> e;
int main()
{
srand(time(NULL));
// Setup Game Window
window.create(sf::VideoMode(WIDTH, HEIGHT), "Space Invaders");
window.setFramerateLimit(60);
// Load Textures
textureLoad();
ship.setUp();
// SetUp Level
for (int i = 0; i < 5; i++) {
e.push_back(enemy = (Enemy)i);
}
// Threads
// Enemy
std::thread th_enemy(EnemyThread);
// Ship
std::thread th_ship(ShipThread);
// Game Loop
while (window.isOpen())
{
// Event Handler
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
// Bullet Handler
for (unsigned int i = 0; i < e.size(); i++) {
if (e[i].b.bExist == true) {
e[i].b.handler();
}
}
// Draw
window.clear();
// Draw Enemy
for (unsigned int i = 0; i < e.size(); i++) {
window.draw(e[i].sprite);
}
// Draw Enemy Bullet
for (unsigned int i = 0; i < e.size(); i++) {
if (e[i].b.bExist == true) {
window.draw(e[i].b.sprite);
}
}
// Draw Ship
window.draw(ship.sprite);
// Draw Ship Bullets
if (ship.b.bExist == true) {
window.draw(ship.b.sprite);
}
window.display();
}
th_enemy.join();
th_ship.join();
return 0;
}
I looked if the sprite was drawn out of the windows but it wans't. I checked if the texture was loaded correctly and it was, also because the bullets of the enemies work fine. I have no idea
PLS HELP ME!! :P
-
I'm wondering why I had to add that line (ship = Ship() Is it the same as ship.Ship() ?). I'm pretty new to c++ so I still have a lot to learn.
Because at the moment when global variable get values (i.e. constructor is called) your texture is not yet loaded (it's loading from file inside main function). No, it's not the same (I'm not even sure that ship.Ship() compiled)
About your bullet sprite problem...
Did you try to add setUp method also for bullet? ;)
-
Because at the moment when global variable get values (i.e. constructor is called) your texture is not yet loaded (it's loading from file inside main function)
You're right, thanks for the explenation
No, it's not the same (I'm not even sure that ship.Ship() compiled)
You're right 2.0. As much as I've understood ship = Ship(); is like calling the constructor. But I was wondering why Ship ship doesn't do the same.
About your bullet sprite problem...
Did you try to add setUp method also for bullet? ;)
You're right 3.0. I thought that the reason why the sprite didn't load was different from the previous one, while in truth was the exact same
;)
-
As much as I've understood ship = Ship(); is like calling the constructor. But I was wondering why Ship ship doesn't do the same.
ship = Ship();
it's assign to variable ship of new object with type Ship.
Ship ship
it's creation of new variable ship with type Ship. Different scopes can have different variables with same name i.e. hidding.
-
Thank you ;D