Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
Introduction to coding - 11. Making the monsters attack

Introduction to coding - 11. Making the monsters attack

Making the monsters attack

Storing information about the player

To keep track of the health and the amount of gold and potions the player has, we define 3 variables:
C++:
int playerGoldAmount = 0, playerPotionAmount = 0, playerHealthAmount = 3;

How the monsters attack

The monsters attack by casting an aura of shadow around them. Basically, when the character is close to a monster, he takes damage over time.

We define a variable to keep track of the amount of time the player has been around a monster and a variable storing whether he is close to a monster or not.
C++:
sf::Clock monsterAttackClock;
bool isPlayerNearMonster = false;

Handling the attack of the monsters

We define a variable representing whether the character is currently close to a monster. By default, we assume he is not.
C++:
bool isPlayerCurrentlyNearMonster = false;
For each monster:
C++:
for(int c=0; c < 4; c++)
{
If it is not dead:
C++:
if(monstersDead[c] == false)
{
We define a sf::FloatRect of 3 times the size of the monster, located around it, that we consider to be the range in which it can deal damage to the player:
C++:
// We copy the sf::FloatRect representing the bounds of the monster.
sf::FloatRect nearMonsterRect(monsters[c].getGlobalBounds());
// We center it.
nearMonsterRect.left -= nearMonsterRect.width;
nearMonsterRect.top -= nearMonsterRect.height;
// We scale it by 3.
nearMonsterRect.width *= 3;
nearMonsterRect.height *= 3;
If the player is in the range of attack of the monster:
C++:
if(player.getGlobalBounds().intersects(nearMonsterRect))
{
We set the variable indicating if he is currently close to a monster to true:
C++:
isPlayerCurrentlyNearMonster = true;
If, before, he was already in the range of attack of the monster:
C++:
if(isPlayerNearMonster)
{
C++:
Then, if the player had been in the range of the aura of the monster for at least 1 second and that he is not already dead:
C++:
if(monsterAttackClock.getElapsedTime().asSeconds() >= 1 && playerHealthAmount > 0)
{
We subtract 1 from the amount of health of the player:
C++:
playerHealthAmount--;
We restart the attack clock of the monsters so there is an other 1 second lap of time before the player loses an other health point:
C++:
monsterAttackClock.restart();
Then, we update the texture of the heart icon:
C++:
if(playerHealthAmount == 0) // He is dead, its last 'heart' is empty.
    hearts[0].setTexture(emptyHeartTex);
else if(playerHealthAmount == 1) // He lost its second 'heart'.
    hearts[1].setTexture(emptyHeartTex);
else // He lost its first 'heart'.
    hearts[2].setTexture(emptyHeartTex);
If the character was not close to a monster before now, we, instead, restart the attack clock of the monsters (From now, if he stays in the range for at least 1 second, he will lose health) and we set the variable indicating whether he is close to a monster to true:
C++:
else
{
    isPlayerNearMonster = true;
    monsterAttackClock.restart();
}
After that, if the variable ' isPlayerCurrentlyNearMonster' kept the value 'false' (The monster is not currently close to a monster), we set the variable ' isPlayerNearMonster' to 'false', so the next time the player is found to be in the attack range of a monster, we know he just entered it:
C++:
if(isPlayerCurrentlyNearMonster == false)
    isPlayerNearMonster = false;
The monsters can now deal damage to the player:
1608531214899.png

The code of the game so far

[CODE lang="cpp" title="main.cpp"]#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;

int playerGoldAmount = 0, playerPotionAmount = 0, playerHealthAmount = 3;

// 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];
sf::Clock monsterAttackClock;
bool isPlayerNearMonster = false;

// Gold

sf::Texture goldTex;
goldTex.loadFromFile("resources/gold.png");

sf::Sprite gold;
gold.setTexture(goldTex);
gold.setScale(2, 2);
gold.setPosition(15, 10);

sf::Font font;
font.loadFromFile("resources/LinuxBiolinum.ttf");

sf::Text goldAmountText;
goldAmountText.setFont(font);
goldAmountText.setString("0");
goldAmountText.setFillColor(sf::Color::White);
goldAmountText.setCharacterSize(24);
goldAmountText.setPosition(60, 10);

// Potions

sf::Texture healthPotionTex;
healthPotionTex.loadFromFile("resources/healthPotion.png");
sf::Texture emptyVialTex;
emptyVialTex.loadFromFile("resources/emptyVial.png");

sf::Sprite potion;
potion.setTexture(emptyVialTex);
potion.setScale(2, 2);
potion.setPosition(350, 10);

sf::Text potionAmountText;
potionAmountText.setFont(font);
potionAmountText.setString("0");
potionAmountText.setFillColor(sf::Color::White);
potionAmountText.setCharacterSize(24);
potionAmountText.setPosition(395, 10);

// Health of the player

sf::Texture heartTex;
heartTex.loadFromFile("resources/heart.png");
sf::Texture emptyHeartTex;
emptyHeartTex.loadFromFile("resources/emptyHeart.png");

sf::Sprite hearts[3];
hearts[0].setPosition(800 - 130, 10);
hearts[1].setPosition(800 - 90, 10);
hearts[2].setPosition(800 - 50, 10);

for(int c = 0; c < 3; c++)
{
hearts[c].setTexture(heartTex);
hearts[c].setScale(2, 2);
}

// 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::Keyboard::isKeyPressed(sf::Keyboard::W))
trans.y -= mouv;
if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
trans.y += mouv;
if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
trans.x -= mouv;
if(sf::Keyboard::isKeyPressed(sf::Keyboard:😀))
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::Keyboard::isKeyPressed(sf::Keyboard::Space) || sf::Mouse::isButtonPressed(sf::Mouse::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);

// Monster attacking

bool isPlayerCurrentlyNearMonster = false;

for(int c=0; c < 4; c++)
{
if(monstersDead[c] == false)
{
sf::FloatRect nearMonsterRect(monsters[c].getGlobalBounds());
nearMonsterRect.left -= nearMonsterRect.width;
nearMonsterRect.top -= nearMonsterRect.height;
nearMonsterRect.width *= 3;
nearMonsterRect.height *= 3;

if(player.getGlobalBounds().intersects(nearMonsterRect))
{
isPlayerCurrentlyNearMonster = true;

if(isPlayerNearMonster)
{
if(monsterAttackClock.getElapsedTime().asSeconds() >= 1 && playerHealthAmount > 0)
{
playerHealthAmount--;
monsterAttackClock.restart();

if(playerHealthAmount == 0)
hearts[0].setTexture(emptyHeartTex);
else if(playerHealthAmount == 1)
hearts[1].setTexture(emptyHeartTex);
else
hearts[2].setTexture(emptyHeartTex);
}
}
else
{
isPlayerNearMonster = true;
monsterAttackClock.restart();
}
}
}
}

if(isPlayerCurrentlyNearMonster == false)
isPlayerNearMonster = false;

// 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.draw(gold);
win.draw(goldAmountText);

win.draw(potion);
win.draw(potionAmountText);

for(int c = 0; c < 3; c++)
win.draw(hearts[c]);

win.display();
}

return 0;
}[/CODE]
Author
LTomy
Views
699
First release
Last update

Ratings

0.00 star(s) 0 ratings
Back
Top Bottom