Recreating Super Mario Bros: Player and Enemy
Recreating Super Mario Bros from Scratch Using C++ and SFML: Player and Enemy
In this post, I will walk you through the process of recreating Super Mario Bros from scratch using C++ and SFML. We'll focus specifically on implementing the player (Mario) and enemy (Goomba and Koopa Troopa) behaviors. This is part of my broader journey into game development, and I'll include code snippets to help illustrate the concepts.
The Player Class
First, let's start by discussing the Player class, which handles all the logic related to Mario's movement, animations, and death sequence.
#include "Player.hpp"
void Player::CheckXMovement_() {
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::Left) && !sf::Keyboard::isKeyPressed(sf::Keyboard::Right) && sf::Keyboard::isKeyPressed(sf::Keyboard::X))
body_.x_speed = 0;
if (body_.is_accelerating && !sf::Keyboard::isKeyPressed(sf::Keyboard::X))
body_.is_accelerating = false;
if (!body_.is_accelerating)
body_.x_speed = 0;
}
This function checks whether Mario is moving left or right and whether the X key (which typically represents running or dashing) is pressed.
Player's Death Animation
The PlayDeathAnimation_ function handles what happens when Mario dies. After Mario dies, his vertical speed is reversed, simulating a jump before falling down.
void Player::PlayDeathAnimation_(float delta_time) {
if (!has_died_ && jump_dead_) {
has_died_ = true;
body_.x_speed = 0;
body_.y_speed = -body_.jump_speed;
}
body_.y_speed += body_.gravity * delta_time;
if (body_.y_speed >= body_.terminal_velocity) {
body_.y_speed = body_.terminal_velocity;
}
player_sprite_.move(0.0f, body_.y_speed * delta_time);
}
Player Constructor
In the constructor, we load the textures and initialize the player states, including normal Mario, Super Mario, and Fire Mario.
Player::Player(const std::string& texture_file) : is_dead_(false), has_died_(false), grace_time_(0.0f), jump_dead_(true), status_(Status::NORMAL) {
if (!player_texture_.loadFromFile(texture_file)) {
// Handle error
}
// Load sprites for small Mario
for (int i = 0; i < 6; i++) {
sf::Sprite sprite;
sprite.setTexture(player_texture_);
sprite.setTextureRect(sf::IntRect(i * 12, 0 * 16, 12, 16));
player_sprites_[Status::NORMAL].push_back(sprite);
}
// Load sprites for Super Mario
for (int i = 0; i < 7; i++) {
sf::Sprite sprite;
sprite.setTexture(player_texture_);
sprite.setTextureRect(sf::IntRect(i * 16, 1 * 32 - 16, 16, 32));
player_sprites_[Status::BIG].push_back(sprite);
}
// Load sprites for Fire Mario
for (int i = 0; i < 7; i++) {
sf::Sprite sprite;
sprite.setTexture(player_texture_);
sprite.setTextureRect(sf::IntRect(i * 16, 2 * 32 - 16, 16, 32));
player_sprites_[Status::FIRE].push_back(sprite);
}
body_.gravity = 1500.0f;
body_.terminal_velocity = 2240.0f;
player_sprite_ = player_sprites_[Status::NORMAL][0];
player_sprite_.setPosition(10, 50);
}
The Player class handles Mario’s different forms (normal, big, and fire), and the game will switch between these forms based on power-ups and player actions.
The Enemy Class
Now, let's move on to the Enemy class, which handles the logic for enemies like Goombas and Koopa Troopas.
Goomba
Here’s the Goomba class, which defines how the enemy behaves when it collides with Mario or the environment:
#include "Enemy.hpp"
#include
Goomba::Goomba(const std::string& texture_file) : Enemy(texture_file) {}
void Goomba::OnJumpedOn() {
is_dead_ = true;
sf::FloatRect bounds = sprite_.getGlobalBounds();
sprite_.setOrigin(bounds.width / 2.0f, bounds.height);
sprite_.setScale(1.0f, 0.2f);
if (!death_timer_started_) {
death_timer_.restart();
death_timer_started_ = true;
}
}
In this method, the Goomba gets flattened when Mario jumps on it, triggering a death animation.
Koopa Troopa
Here’s the Koopa Troopa class, which behaves differently from Goombas. Koopa Troopas retreat into their shells when Mario jumps on them, and the player can kick the shell to defeat other enemies.
KoopaTroopa::KoopaTroopa(const std::string& texture_file)
: Enemy(texture_file) {}
void KoopaTroopa::OnJumpedOn() {
if (!in_shell_) {
// Go into shell if not already
in_shell_ = true;
} else if (in_shell_ && !is_shell_moving_) {
// Start moving the shell
is_shell_moving_ = true;
body_.x_speed = 200.0f; // Shell speed
} else {
// Stop the shell if it is moving
is_shell_moving_ = false;
body_.x_speed = 0;
}
}
Koopa Troopas enter a defensive shell state when jumped on and can be used strategically to attack other enemies.
Conclusion
Recreating Super Mario Bros using C++ and SFML is a rewarding project that teaches valuable skills in game development, including sprite management, animation, and collision detection. This post only covers the player and enemy mechanics, but you can build upon this foundation to create a fully functioning game.
Happy coding, and stay tuned for more!
Comments Reply