Making the character attack
View attachment 615Keeping track of whether a monster is alive or not
A variable of type 'bool' can either equal 'true' or 'false'. Here, we define an array of 'bool' values indicating if a monster is dead or not:
C++:
bool monstersDead[4];
C++:
for(int c = 0; c < 4; c++)
{
monsters[c].setTexture(monsterTexs[0]);
monsters[c].setScale(3, 3);
monstersDead[c] = false;
}
C++:
for(int c = 0; c < 4; c++)
{
if(monstersDead[c] == false)
win.draw(monsters[c]);
}
C++:
sf::Clock monsterRespawnClocks[4];
Control structures with many conditions
There may be more than one condition inside a control structure (Example: The structure 'if', 'while', 'for'...).To test if two conditions are true, we must separate them with two ampersands &&.
The following code tests if 'var1' equals 7 AND 'var2' is bigger than 13. The instructions inside the braces are executed only if both conditions are true:
C++:
if(var1 == 7 && var2 > 13)
{
}
The following code tests if 'var1' is smaller than 66 AND/OR 'var2' equals 73. The instructions inside the braces are executed as long as at least one of the conditions is true:
C++:
if(var1 < 66 || var2 == 73)
{
}
C++:
if((var1 > 10 || var2 > 20) && (var1 < 100 || var2 < 160))
{
}
C++:
if(var <= 10)
{
}
The attack animation
When the character attacks, we temporarily change its texture. Let us load that texture:
C++:
sf::Texture playerAttackingTex;
playerAttackingTex.loadFromFile("resources/playerAttacking.png");
C++:
sf::Clock playerAttackClock;
C++:
// We check if the left button of the mouse is currently pressed.
if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
}
C++:
if((sf::Keyboard::isKeyPressed(sf::Keyboard::Space) || sf::Mouse::isButtonPressed(sf::Mouse::Left)) && playerAttackClock.getElapsedTime().asMilliseconds() > 1000)
{
}
C++:
playerAttackClock.restart();
C++:
player.setTexture(playerAttackingTex);
C++:
sf::FloatRect attackRect(player.getPosition()+sf::Vector2f((player.getGlobalBounds().width/4), -player.getGlobalBounds().height/3), sf::Vector2f(player.getGlobalBounds().width/4, player.getGlobalBounds().height/3));
C++:
for(int c=0; c < 4; c++)
{
// If the monster is not dead and the attack touched it.
if(monstersDead[c] == false && attackRect.intersects(monsters[c].getGlobalBounds()))
{
// The monster is hit by the attack, so he dies.
monstersDead[c] = true;
// To know when to respawn it, we keep track of the time elapsed since the monster died.
monsterRespawnClocks[c].restart();
}
}
C++:
if(playerAttackClock.getElapsedTime().asMilliseconds() > 1000)
{
player.setTexture(playerTex);
}
Spawning back the monsters that died
In the game loop, for each monster, we check if the time elapsed since its death is of at least 8 seconds. If so, set it back to life.
C++:
for(int c=0; c < 4; c++)
{
if(monsterRespawnClocks[c].getElapsedTime().asSeconds() >= 8)
monstersDead[c] = false;
}
The code of the game so far
C++:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow win(sf::VideoMode(800, 600), "Introduction to coding", sf::Style::Close);
// Grass
sf::Texture grassTex;
grassTex.loadFromFile("resources/grass.png");
grassTex.setRepeated(true);
sf::Sprite grass;
grass.setTexture(grassTex);
grass.setScale(5, 5);
grass.setTextureRect(sf::IntRect(0,0,160,120));
// Dirt tiles
sf::Texture dirtTex;
dirtTex.loadFromFile("resources/dirt.png");
sf::Sprite dirtTiles[10];
dirtTiles[0].setPosition(300, 60);
dirtTiles[1].setPosition(660, 300);
dirtTiles[2].setPosition(390, 480);
dirtTiles[3].setPosition(660, 60);
dirtTiles[4].setPosition(720, 420);
dirtTiles[5].setPosition(180, 300);
dirtTiles[6].setPosition(60, 180);
dirtTiles[7].setPosition(60, 420);
dirtTiles[8].setPosition(240, 420);
dirtTiles[9].setPosition(480, 120);
for(int c = 0; c < 10; c++)
{
dirtTiles[c].setTexture(dirtTex);
dirtTiles[c].setScale(5, 5);
}
// Trees
sf::Texture treeTex;
treeTex.loadFromFile("resources/tree.png");
sf::Sprite trees[8];
trees[0].setPosition(50, 60);
trees[1].setPosition(600, 150);
trees[2].setPosition(630, 456);
trees[3].setPosition(300, 600);
trees[4].setPosition(720, 40);
trees[5].setPosition(460, 500);
trees[6].setPosition(20, 370);
trees[7].setPosition(340, 240);
for(int c = 0; c < 8; c++)
{
trees[c].setTexture(treeTex);
trees[c].setScale(3, 3);
}
// Stones
sf::Texture stoneTex;
stoneTex.loadFromFile("resources/stone.png");
sf::Sprite stones[3];
stones[0].setPosition(304, 64);
stones[1].setPosition(664, 304);
stones[2].setPosition(180, 300);
for(int c = 0; c < 3; c++)
{
stones[c].setTexture(stoneTex);
stones[c].setScale(3, 3);
}
// Player
sf::Texture playerTex;
playerTex.loadFromFile("resources/player.png");
sf::Texture playerAttackingTex;
playerAttackingTex.loadFromFile("resources/playerAttacking.png");
sf::Sprite player;
player.setTexture(playerTex);
player.setPosition(400, 300);
player.setScale(3, 3);
float playerMovementSpeed = 200.0;
sf::Clock playerMovementClock;
sf::Clock playerAttackClock;
// Monsters
sf::Texture monsterTexs[3];
monsterTexs[0].loadFromFile("resources/monster1.png");
monsterTexs[1].loadFromFile("resources/monster2.png");
monsterTexs[2].loadFromFile("resources/monster3.png");
sf::Sprite monsters[4];
monsters[0].setPosition(500, 40);
monsters[1].setPosition(100, 170);
monsters[2].setPosition(400, 450);
monsters[3].setPosition(690, 160);
bool monstersDead[4];
for(int c = 0; c < 4; c++)
{
monsters[c].setTexture(monsterTexs[0]);
monsters[c].setScale(3, 3);
monstersDead[c] = false;
}
sf::Clock monstersAnimationClock;
sf::Clock monsterRespawnClocks[4];
// Game loop
sf::Event event;
while(win.isOpen())
{
while(win.pollEvent(event))
{
if(event.type == sf::Event::Closed)
win.close();
}
// Player movements
sf::Vector2f trans;
float mouv = playerMovementClock.getElapsedTime().asSeconds() * playerMovementSpeed;
if(sf:⌨️:isKeyPressed(sf:⌨️:W))
trans.y -= mouv;
if(sf:⌨️:isKeyPressed(sf:⌨️:S))
trans.y += mouv;
if(sf:⌨️:isKeyPressed(sf:⌨️:A))
trans.x -= mouv;
if(sf:⌨️:isKeyPressed(sf:⌨️:D))
trans.x += mouv;
player.setPosition(player.getPosition().x+trans.x, player.getPosition().y+trans.y);
playerMovementClock.restart();
// Collision testing
for(int c=0; c < 8; c++)
{
if(player.getGlobalBounds().intersects(trees[c].getGlobalBounds()))
player.setPosition(player.getPosition().x-trans.x, player.getPosition().y-trans.y);
}
for(int c=0; c < 3; c++)
{
if(player.getGlobalBounds().intersects(stones[c].getGlobalBounds()))
player.setPosition(player.getPosition().x-trans.x, player.getPosition().y-trans.y);
}
// Animation of the monsters
int index = monstersAnimationClock.getElapsedTime().asMilliseconds() / 333;
if(index > 2)
{
monstersAnimationClock.restart();
index = 0;
}
for(int c=0; c < 4; c++)
{
monsters[c].setTexture(monsterTexs[index]);
}
// Player attacking
if((sf:⌨️:isKeyPressed(sf:⌨️:Space) || sf:🐭:isButtonPressed(sf:🐭:Left)) && playerAttackClock.getElapsedTime().asMilliseconds() > 1000)
{
playerAttackClock.restart();
player.setTexture(playerAttackingTex);
// Testing if the attack reaches a monster
sf::FloatRect attackRect(player.getPosition()+sf::Vector2f((player.getGlobalBounds().width/4), -player.getGlobalBounds().height/3), sf::Vector2f(player.getGlobalBounds().width/4, player.getGlobalBounds().height/3));
for(int c=0; c < 4; c++)
{
if(monstersDead[c] == false && attackRect.intersects(monsters[c].getGlobalBounds()))
{
monstersDead[c] = true;
monsterRespawnClocks[c].restart();
}
}
}
if(playerAttackClock.getElapsedTime().asMilliseconds() > 1000)
{
player.setTexture(playerTex);
}
// Respawn of the monsters
for(int c=0; c < 4; c++)
{
if(monsterRespawnClocks[c].getElapsedTime().asSeconds() >= 8)
monstersDead[c] = false;
}
// Rendering
win.clear();
win.draw(grass);
for(int c = 0; c < 10; c++)
win.draw(dirtTiles[c]);
for(int c = 0; c < 8; c++)
win.draw(trees[c]);
for(int c = 0; c < 3; c++)
win.draw(stones[c]);
win.draw(player);
for(int c = 0; c < 4; c++)
{
if(monstersDead[c] == false)
win.draw(monsters[c]);
}
win.display();
}
return 0;
}
Last edited by a moderator: