Skip to content

Commit

Permalink
Menu style overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisD3D committed Apr 15, 2024
1 parent 28cc5d3 commit 0ff7edb
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 58 deletions.
Binary file added images/drive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/game_played.otf
Binary file not shown.
52 changes: 40 additions & 12 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,103 @@
# SnakeQT

###### Snake game in C++ using QT5 - Denis DAVIAUD & Augustin ROUH - INSA CVL 2024

## Build

1. [Download and install](https://cmake.org/resources/) cmake (version 3.11 or later recommended) and mingw.
2. Install Qt5 (recommended version 5.15.2)
On linux
On linux

```bash
sudo apt-get install qt5-default
# ou
pip install aqtinstall
aqt install-qt linux desktop 5.15.2 gcc_64
```

On Windows

```bash
pip install aqtinstall
aqt install-qt windows desktop 5.15.2 win64_mingw81
```

3. Clone the repository (including submodules) with the following command:

```bash
git clone --recurse-submodules https://github.com/DenisD3D/SnakeQT.git
```
4. If qt is installed through aqtinstall, set the environment variable `Qt5_DIR` to the directory containing the `Qt5Config.cmake` file (e.g. `C:\Qt\5.15.2\mingw81_64\lib\cmake\Qt5`)

4. If qt is installed through aqtinstall, set the environment variable `Qt5_DIR` to the directory containing
the `Qt5Config.cmake` file (e.g. `C:\Qt\5.15.2\mingw81_64\lib\cmake\Qt5`)
5. Run cmake in the root directory of the project.

```bash
cmake -S . -B build
cmake --build build
```
6. On Windows, define QT_QPA_PLATFORM_PLUGIN_PATH to the directory containing the `qwindows.dll` file (e.g. `C:\Qt\5.15.2\mingw81_64\plugins\platforms`) before running SnakeQT.exe.
7. Run the executable in the build directory.

6. On Windows, define QT_QPA_PLATFORM_PLUGIN_PATH to the directory containing the `qwindows.dll` file (
e.g. `C:\Qt\5.15.2\mingw81_64\plugins\platforms`) before running SnakeQT.exe.
7. Run the executable in the build directory.

## Features

- File based map loading
- Embedded custom texture in map files
- In-game map editor for maps up to 30x30
- In-game map browser and downloader from remote servers
- Online high score system computed from apple and bonus eaten
- Online high score system computed from apple and bonus eaten
- Full screen support


## Map File format

Map are stored as a zip file ending with .skm. The zip file contains the following files:

- `map.xml`: a xml file containing the map data

```xml
<?xml version="1.0" encoding="utf-8" ?>
<!-- Define map properties: size, name, apple texture and default tile -->
<map width="10" height="10" apple_texture="custom_apple.png" name="Map map" author="Map Author"
description="Map description" default_tile="custom_ground">
<!-- Define snake properties: start position & length, textures -->
<snake init_x="7" init_y="7" init_length="2" init_direction="up" head_texture="custom_snake_head.png" body_texture="custom_snake_body.png"/>
<snake init_x="7" init_y="7" init_length="2" init_direction="up" head_texture="custom_snake_head.png"
body_texture="custom_snake_body.png"/>

<!-- Define a custom tile type with its own texture. type is an integer defining properties as flags (WALKABLE = 1 << 0, APPLE_SPAWN = 1 << 1, BONUS_SPAWN = 1 << 2 -->
<type name="custom_ground" texture="custom_ground.png" type="7"/>

<!-- Set the tile at x, y to the cell of type name. Tile that aren't set default to default_tile -->
<tile x="0" y="0" cell="custom_wall"/>
</map>
```

- All the textures used in the map, placed as png file at the root of the zip file

## Possible improvement

- Use map name, display author and high score in the map browser (as return by API)
- Add a campaign mode where game switch to the next map when a certain score is reached
- Support for adaptive textures (choosing the right texture depending on the surrounding tiles) instead of loading all 9
textures as separate ones

## Tile preprocessing using imageMagick
Some tiles aren't in the right size or contains transparency. To fix this, the following command can be used to fix all images in subfolders of the current directory:

The tiles texture used in the game come from https://kenney.nl/ assets. Some textures aren't in the right size for the
game or might contain transparency. To fix this, the following command can be used to fix all images in subfolders of
the current directory:

```bash
find . -name '*.png' -exec convert {} -scale 32x32 -background "#ecb06e" -alpha remove -alpha off {} \;
```

Replace #ecb06e by the background color of your map
Extract a spritesheet into individual images:
If all textures are on the same spritesheet, they can be extracted into individual images using:

```bash
magick convert roguelikeSheet_transparent.png -crop 57x31-1-1@ +repage +adjoin spaced-1_%d.png
```
Where 57 is the number of sprite in the x-axis and 31 in the y-axis and -1-1 is the offset between each sprite

Tested on some of https://kenney.nl/ assets
Where 57 is the number of sprite in the x-axis and 31 in the y-axis and -1-1 is the offset between each sprite.

2 changes: 2 additions & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
<file>images/wall.png</file>
<file>images/apple.png</file>
<file>images/cloud.png</file>
<file>images/drive.png</file>
<file>images/game_played.otf</file>
</qresource>
</RCC>
54 changes: 43 additions & 11 deletions src/screens/browsemapscreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,64 @@
* Construct map browser screen.
*/
BrowseMapScreen::BrowseMapScreen(QWidget *parent): QWidget(parent) {
const auto layout = new QVBoxLayout(this);
// Load custom font
const int id = QFontDatabase::addApplicationFont(":/images/game_played.otf");
const QString family = QFontDatabase::applicationFontFamilies(id).at(0);
QFont snakefont(family);
snakefont.setPointSize(20);

const auto layoutV = new QVBoxLayout(this);
const auto layoutH = new QHBoxLayout;

const auto helpText = new QLabel("Select a map to play, maps with a cloud icon are stored online and will be downloaded:", this);

const auto backButton = new QPushButton("Main Menu", this);
backButton->setFont(snakefont);
backButton->setStyleSheet("padding-left: 20px; padding-right: 20px;");
backButton->setFixedSize(200, 50);

const auto openMapFolderButton = new QPushButton("Open map folder", this);
openMapFolderButton->setFont(snakefont);
openMapFolderButton->setStyleSheet("font-size: 17px;");
openMapFolderButton->setFixedSize(200, 50);

const auto backButton = new QPushButton("Back", this);
mapList = new QListWidget(this);
loadButton = new QPushButton("Play", this);
loadButton->setFont(snakefont);
loadButton->setStyleSheet("padding-left: 20px; padding-right: 20px;");
loadButton->setFixedSize(200, 50);

mapList = new QListWidget(this);

layoutH->addWidget(backButton);
layoutH->addStretch();
layoutH->addWidget(openMapFolderButton);
layoutH->addStretch(2);
layoutH->addWidget(loadButton);

layoutV->addWidget(helpText);
layoutV->addWidget(mapList);
layoutV->addLayout(layoutH);

layout->addWidget(backButton);
layout->addWidget(mapList);
layout->addWidget(loadButton);

connect(backButton, &QPushButton::clicked, this, &BrowseMapScreen::backClicked);
connect(loadButton, &QPushButton::clicked, this, &BrowseMapScreen::loadClicked);
connect(mapList, &QListWidget::itemSelectionChanged, this, &BrowseMapScreen::mapSelectionChanged);
connect(openMapFolderButton, &QPushButton::clicked, []() {
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("maps").absoluteFilePath()));
});

// Load maps
// Load local maps list
QDirIterator it("maps", QDir::Files);
QPixmap transparentPixmap(24, 24);
transparentPixmap.fill(Qt::transparent);
const QIcon spacerIcon(transparentPixmap);
const QIcon driveIcon(":/images/drive.png");
while (it.hasNext()) {
it.next();
const auto item = new QListWidgetItem(it.fileName());
item->setIcon(spacerIcon);
item->setIcon(driveIcon);
item->setData(Qt::UserRole, true);
mapList->addItem(item);
}

// Load online maps list
loadOnlineMaps();
}

Expand Down Expand Up @@ -70,6 +101,7 @@ void BrowseMapScreen::loadClicked() {
void BrowseMapScreen::mapSelectionChanged() {
if (mapList->currentItem() != nullptr) {
loadButton->setText(mapList->currentItem()->data(Qt::UserRole).toBool() ? "Play" : "Download and Play");
loadButton->setStyleSheet(mapList->currentItem()->data(Qt::UserRole).toBool() ? "font-size: 20px;" : "font-size: 17px;");
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/screens/browsemapscreen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class BrowseMapScreen final : public QWidget {
Q_OBJECT
QListWidget *mapList;
QPushButton *loadButton;
FileDownloader *downloader;
FileDownloader *downloader = nullptr;

void loadOnlineMaps();

Expand Down
6 changes: 6 additions & 0 deletions src/screens/editorscreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ EditorScreen::EditorScreen(const QString &file_info, const bool create_map, QWid
snakeInitDirection->setCurrentIndex(map.getInitDirection());
auto *saveButton = new QPushButton("Save map");
auto *saveAsButton = new QPushButton("Save map as");
auto *backButton = new QPushButton("Main Menu");

auto *mapNameLabel = new QLabel("Map Name");
auto *mapAuthorLabel = new QLabel("Map Author");
Expand Down Expand Up @@ -64,6 +65,7 @@ EditorScreen::EditorScreen(const QString &file_info, const bool create_map, QWid
leftLayout->addWidget(snakeInitDirection);
leftLayout->addWidget(saveButton);
leftLayout->addWidget(saveAsButton);
leftLayout->addWidget(backButton);

connect(mapName, &QLineEdit::editingFinished, [this, mapName] {
map.setName(mapName->text());
Expand Down Expand Up @@ -148,6 +150,10 @@ EditorScreen::EditorScreen(const QString &file_info, const bool create_map, QWid
}
});

connect(backButton, &QPushButton::clicked, [this] {
emit back();
});

auto *rightLayout = new QVBoxLayout;

auto *tileTypeLabel = new QLabel("Tile Types");
Expand Down
3 changes: 3 additions & 0 deletions src/screens/editorscreen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,8 @@ public slots:
void showTileSelectorContextMenu(const QPoint &pos);

void placeTileAtPosition(Position pos, bool is_right_click);

signals:
void back();
};
#endif //EDITORSCREEN_HPP
55 changes: 44 additions & 11 deletions src/screens/mainmenu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,61 @@ class MainMenu final : public QWidget {

public:
explicit MainMenu(QWidget *parent = nullptr) : QWidget(parent) {
const auto layout = new QVBoxLayout(this);
// Load custom font
const int id = QFontDatabase::addApplicationFont(":/images/game_played.otf");
const QString family = QFontDatabase::applicationFontFamilies(id).at(0);
QFont snakefont(family);
snakefont.setPointSize(20);

const auto startButton = new QPushButton("Start Game", this);
startButton->setDisabled(true);
const auto browseMapButton = new QPushButton("Browse Map", this);
const auto layoutH = new QHBoxLayout(this);

const auto layoutV = new QVBoxLayout;

// Title
const auto title = new QLabel("SnakeQT", this);
title->setFont(snakefont);
title->setStyleSheet("font-size: 70px;");
title->setAlignment(Qt::AlignCenter);
const auto subTitle = new QLabel("by Denis Daviaud & Augustin Rouh", this);
subTitle->setFont(snakefont);
subTitle->setStyleSheet("font-size: 12px;");
subTitle->setAlignment(Qt::AlignCenter);

// Buttons
const auto playButton = new QPushButton("Play a map", this);
playButton->setFont(snakefont);
playButton->setStyleSheet("padding-left: 20px; padding-right: 20px;");
const auto mapEditor = new QPushButton("Create or edit a map", this);
mapEditor->setFont(snakefont);
mapEditor->setStyleSheet("padding-left: 20px; padding-right: 20px;");
const auto exitButton = new QPushButton("Exit", this);
exitButton->setFont(snakefont);
exitButton->setStyleSheet("padding-left: 20px; padding-right: 20px;");

layoutV->addStretch();
layoutV->addWidget(title);
layoutV->addWidget(subTitle);
layoutV->addStretch();
layoutV->addWidget(playButton);
layoutV->addWidget(mapEditor);
layoutV->addWidget(exitButton);
layoutV->addStretch();

layout->addWidget(startButton);
layout->addWidget(browseMapButton);
layout->addWidget(exitButton);
layoutH->addStretch();
layoutH->addLayout(layoutV);
layoutH->addStretch();

connect(startButton, &QPushButton::clicked, this, &MainMenu::startGameClicked);
connect(browseMapButton, &QPushButton::clicked, this, &MainMenu::browseMapClicked);
connect(playButton, &QPushButton::clicked, this, &MainMenu::playMapClicked);
connect(mapEditor, &QPushButton::clicked, this, &MainMenu::createOrEditMapClicked);
connect(exitButton, &QPushButton::clicked, this, &MainMenu::exitClicked);
}

signals:
void startGameClicked();
void playMapClicked();

void exitClicked();

void browseMapClicked();
void createOrEditMapClicked();
};


Expand Down
Loading

0 comments on commit 0ff7edb

Please sign in to comment.