Нужна помощь в оптимизации игры на c++ sfml
У меня есть такой проект на c++ sfml 2.6.2. Я не особо сильно знаю c++, да и sfml тоже. Нужно для реализации проекта использовать. Сначала код писал на основном компьютере, все было хорошо. Но потом перенес на не очень мощный ноутбук (даже слабенький) и стало долго загружать. (Изначальное поле 64х64, вообще не грузило). Нужна помощь в оптимизации. ( если это возможно)
это cell.h
#ifndef CELL_H
#define CELL_H
#include <SFML/Graphics.hpp>
enum class CellState
{
Empty,
Cross,
Circle
};
class Cell
{
private:
CellState state;
sf::Sprite sprite;
sf::Texture emptyTexture;
sf::Texture crossTexture;
sf::Texture circleTexture;
int x, y;
public:
Cell();
bool loadTextures();
void setPosition(int x, int y);
void setState(CellState newState);
CellState getState() const;
void draw(sf::RenderWindow& window, const sf::Vector2f& cameraOffset);
bool contains(const sf::Vector2f& point, const sf::Vector2f& cameraOffset)
const;
sf::Vector2i getGridPosition() const;
};
#endif
cell.cpp
#include "cell.h"
Cell::Cell() : state(CellState::Empty), x(0), y(0)
{
}
bool Cell::loadTextures()
{
if (!emptyTexture.loadFromFile("empty128.png") ||
!crossTexture.loadFromFile("krest128.png") ||
!circleTexture.loadFromFile("circle128.png"))
{
return false;
}
sprite.setTexture(emptyTexture);
}
void Cell::setPosition(int x, int y)
{
this->x = x;
this->y = y;
sprite.setPosition(static_cast<float>(x * 128), static_cast<float>(y * 128));
}
void Cell::setState(CellState newState)
{
state = newState;
switch (state)
{
case CellState::Empty:
sprite.setTexture(emptyTexture);
break;
case CellState::Cross:
sprite.setTexture(crossTexture);
break;
case CellState::Circle:
sprite.setTexture(circleTexture);
break;
}
}
CellState Cell::getState() const
{
return state;
}
void Cell::draw(sf::RenderWindow& window, const sf::Vector2f& cameraOffset)
{
sf::Vector2f originalPos = sprite.getPosition();
sprite.setPosition(originalPos + cameraOffset);
window.draw(sprite);
sprite.setPosition(originalPos);
}
bool Cell::contains(const sf::Vector2f& point, const sf::Vector2f& cameraOffset)
const
{
sf::FloatRect bounds = sprite.getGlobalBounds();
bounds.left += cameraOffset.x;
bounds.top += cameraOffset.y;
return bounds.contains(point);
}
sf::Vector2i Cell::getGridPosition() const
{
return sf::Vector2i(x, y);
}
board.h
#ifndef BOARD_H
#define BOARD_H
#include "cell.h"
#include <SFML/Graphics.hpp>
class Board {
private:
Cell** cells;
int width;
int height;
int offsetX;
int offsetY;
bool isCrossTurn;
bool gameFinished;
CellState winner;
void expandBoardIfNeeded(int worldX, int worldY);
void expandBoard(int newWidth, int newHeight, int newOffsetX, int newOffsetY);
void cleanup();
bool checkWin(int worldX, int worldY, CellState state) const;
bool checkDirection(int worldX, int worldY, int dx, int dy, CellState state)
const;
public:
Board();
~Board();
bool initialize();
void makeMove(int worldX, int worldY);
void draw(sf::RenderWindow& window, const sf::Vector2f& cameraOffset);
void updateVisibleArea(const sf::Vector2f& cameraOffset, const sf::Vector2u&
windowSize);
bool handleClick(const sf::Vector2f& mousePos, const sf::Vector2f&
cameraOffset);
bool isGameFinished() const;
CellState getWinner() const;
bool getCurrentPlayer() const;
void resetGame();
};
#endif
board.cpp
#include "board.h"
#include <cmath>
#include <iostream>
#include <algorithm>
Board::Board() : cells(nullptr), width(0), height(0),
offsetX(0), offsetY(0),
isCrossTurn(true), gameFinished(false),
winner(CellState::Empty) {}
Board::~Board()
{
cleanup();
}
void Board::cleanup()
{
if (cells)
{
for (int i = 0; i < width; ++i)
{
delete[] cells[i];
}
delete[] cells;
cells = nullptr;
}
width = 0;
height = 0;
}
bool Board::initialize()
{
cleanup();
// Начинаем с поля 64x64
width = 32;
height = 32;
offsetX = -16; // Центрируем поле
offsetY = -16;
cells = new Cell * [width];
for (int i = 0; i < width; ++i)
{
cells[i] = new Cell[height];
for (int j = 0; j < height; ++j)
{
if (!cells[i][j].loadTextures())
{
std::cerr << "Failed to load textures for cell (" << i << ", " << j <<
")" << std::endl;
cleanup();
return false;
}
int worldX = i + offsetX;
int worldY = j + offsetY;
cells[i][j].setPosition(worldX, worldY);
}
}
std::cout << "Board initialized: " << width << "x" << height
<< " at offset (" << offsetX << ", " << offsetY << ")" << std::endl;
return true;
}
void Board::expandBoardIfNeeded(int worldX, int worldY)
{
// Проверяем, нужно ли расширять поле
bool needExpand = false;
int newOffsetX = offsetX;
int newOffsetY = offsetY;
int newWidth = width;
int newHeight = height;
// Проверяем границы и расширяем если нужно с запасом
if (worldX < offsetX) {
int expandLeft = offsetX - worldX + 16; // +16 запас
newOffsetX -= expandLeft;
newWidth += expandLeft;
needExpand = true;
}
else if (worldX >= offsetX + width) {
int expandRight = worldX - (offsetX + width) + 16;
newWidth += expandRight;
needExpand = true;
}
if (worldY < offsetY) {
int expandTop = offsetY - worldY + 16;
newOffsetY -= expandTop;
newHeight += expandTop;
needExpand = true;
}
else if (worldY >= offsetY + height) {
int expandBottom = worldY - (offsetY + height) + 16;
newHeight += expandBottom;
needExpand = true;
}
if (needExpand) {
std::cout << "Expanding board to: " << newWidth << "x" << newHeight
<< " at offset (" << newOffsetX << ", " << newOffsetY << ")" << std::endl;
expandBoard(newWidth, newHeight, newOffsetX, newOffsetY);
}
}
void Board::expandBoard(int newWidth, int newHeight, int newOffsetX, int newOffsetY)
{
// Создаем новый массив
Cell** newCells = new Cell * [newWidth];
for (int i = 0; i < newWidth; ++i)
{
newCells[i] = new Cell[newHeight];
for (int j = 0; j < newHeight; ++j)
{
int worldX = i + newOffsetX;
int worldY = j + newOffsetY;
if (!newCells[i][j].loadTextures())
{
std::cerr << "Failed to load textures during expansion" << std::endl;
// Очистка в случае ошибки
for (int x = 0; x <= i; ++x)
{
delete[] newCells[x];
}
delete[] newCells;
return;
}
newCells[i][j].setPosition(worldX, worldY);
// Копируем состояние из старого массива, если ячейка существует в старых границах
if (cells) {
int oldI = worldX - offsetX;
int oldJ = worldY - offsetY;
if (oldI >= 0 && oldI < width && oldJ >= 0 && oldJ < height)
{
CellState oldState = cells[oldI][oldJ].getState();
newCells[i][j].setState(oldState);
}
}
}
}
// Заменяем старый массив
cleanup();
cells = newCells;
width = newWidth;
height = newHeight;
offsetX = newOffsetX;
offsetY = newOffsetY;
}
void Board::makeMove(int worldX, int worldY)
{
if (gameFinished) return;
// Проверяем и расширяем поле если нужно
expandBoardIfNeeded(worldX, worldY);
// Преобразуем мировые координаты в локальные
int localX = worldX - offsetX;
int localY = worldY - offsetY;
if (localX < 0 || localX >= width || localY < 0 || localY >= height)
{
std::cerr << "Cell out of bounds after expansion: (" << worldX << ", " << worldY
<< ")" << std::endl;
return;
}
if (cells[localX][localY].getState() == CellState::Empty)
{
cells[localX][localY].setState(isCrossTurn ? CellState::Cross :
CellState::Circle);
if (checkWin(worldX, worldY, cells[localX][localY].getState()))
{
gameFinished = true;
winner = cells[localX][localY].getState();
}
isCrossTurn = !isCrossTurn;
}
}
bool Board::checkDirection(int worldX, int worldY, int dx, int dy, CellState state)
const {
if (!cells) return false;
int count = 1;
// Проверяем в положительном направлении
for (int i = 1; i < 5; ++i) {
int checkX = worldX + i * dx;
int checkY = worldY + i * dy;
int localX = checkX - offsetX;
int localY = checkY - offsetY;
if (localX < 0 || localX >= width || localY < 0 || localY >= height)
{
break;
}
if (cells[localX][localY].getState() != state)
{
break;
}
count++;
}
// Проверяем в отрицательном направлении
for (int i = 1; i < 5; ++i)
{
int checkX = worldX - i * dx;
int checkY = worldY - i * dy;
int localX = checkX - offsetX;
int localY = checkY - offsetY;
if (localX < 0 || localX >= width || localY < 0 || localY >= height)
{
break;
}
if (cells[localX][localY].getState() != state)
{
break;
}
count++;
}
return count >= 5;
}
bool Board::checkWin(int worldX, int worldY, CellState state) const
{
if (!cells) return false;
return checkDirection(worldX, worldY, 1, 0, state) || // Horizontal
checkDirection(worldX, worldY, 0, 1, state) || // Vertical
checkDirection(worldX, worldY, 1, 1, state) || // Diagonal
checkDirection(worldX, worldY, 1, -1, state); // Diagonal /
}
void Board::draw(sf::RenderWindow& window, const sf::Vector2f& cameraOffset)
{
if (!cells) return;
// Определяем видимую область в мировых координатах
int startWorldX = static_cast<int>((-cameraOffset.x) / 128) - 2;
int endWorldX = static_cast<int>((-cameraOffset.x + window.getSize().x) / 128) + 2;
int startWorldY = static_cast<int>((-cameraOffset.y) / 128) - 2;
int endWorldY = static_cast<int>((-cameraOffset.y + window.getSize().y) / 128) + 2;
// Преобразуем в локальные координаты с проверкой границ
int startLocalX = std::max(0, startWorldX - offsetX);
int endLocalX = std::min(width - 1, endWorldX - offsetX);
int startLocalY = std::max(0, startWorldY - offsetY);
int endLocalY = std::min(height - 1, endWorldY - offsetY);
// Рисуем только видимые ячейки
for (int i = startLocalX; i <= endLocalX; ++i)
{
for (int j = startLocalY; j <= endLocalY; ++j)
{
cells[i][j].draw(window, cameraOffset);
}
}
}
void Board::updateVisibleArea(const sf::Vector2f& cameraOffset, const sf::Vector2u&
windowSize)
{
// Предварительно расширяем поле для видимой области
int startWorldX = static_cast<int>((-cameraOffset.x) / 128) - 8; // Запас
int endWorldX = static_cast<int>((-cameraOffset.x + windowSize.x) / 128) + 8;
int startWorldY = static_cast<int>((-cameraOffset.y) / 128) - 8;
int endWorldY = static_cast<int>((-cameraOffset.y + windowSize.y) / 128) + 8;
// Проверяем углы видимой области
expandBoardIfNeeded(startWorldX, startWorldY);
expandBoardIfNeeded(endWorldX, startWorldY);
expandBoardIfNeeded(startWorldX, endWorldY);
expandBoardIfNeeded(endWorldX, endWorldY);
}
bool Board::handleClick(const sf::Vector2f& mousePos, const sf::Vector2f& cameraOffset)
{
if (!cells) return false;
// Правильно преобразуем координаты мыши в мировые координаты сетки
// mousePos - это координаты мыши в окне
// cameraOffset - это смещение камеры
// Правильная формула: worldX = (mousePos.x - cameraOffset.x) / cellSize
float worldXFloat = (mousePos.x - cameraOffset.x) / 128.0f;
float worldYFloat = (mousePos.y - cameraOffset.y) / 128.0f;
// Округляем до ближайшей ячейки
int worldX = static_cast<int>(std::round(worldXFloat));
int worldY = static_cast<int>(std::round(worldYFloat));
// Для отрицательных координат нужно правильно округлять
if (worldXFloat < 0)
{
worldX = static_cast<int>(std::floor(worldXFloat));
}
if (worldYFloat < 0)
{
worldY = static_cast<int>(std::floor(worldYFloat));
}
std::cout << "Click at mouse (" << mousePos.x << ", " << mousePos.y
<< "), camera (" << cameraOffset.x << ", " << cameraOffset.y
<< "), world (" << worldX << ", " << worldY << ")" << std::endl;
// Проверяем, попадает ли клик в какую-либо ячейку
bool found = false;
int startWorldX = worldX - 1;
int endWorldX = worldX + 1;
int startWorldY = worldY - 1;
int endWorldY = worldY + 1;
for (int checkX = startWorldX; checkX <= endWorldX && !found; ++checkX)
{
for (int checkY = startWorldY; checkY <= endWorldY && !found; ++checkY)
{
int localX = checkX - offsetX;
int localY = checkY - offsetY;
if (localX >= 0 && localX < width && localY >= 0 && localY < height)
{
// Создаем точку в мировых координатах для проверки попадания
sf::Vector2f checkPoint(
static_cast<float>(checkX * 128 + cameraOffset.x + 64), // центр ячейки
static_cast<float>(checkY * 128 + cameraOffset.y + 64)
);
// Проверяем расстояние до центра ячейки
float distance = std::sqrt(
std::pow(mousePos.x - checkPoint.x, 2) +
std::pow(mousePos.y - checkPoint.y, 2)
);
if (distance < 64.0f) { // Если в пределах ячейки
worldX = checkX;
worldY = checkY;
found = true;
std::cout << "Found cell at (" << worldX << ", " << worldY << ")" <<
std::endl;
}
}
}
}
if (found)
{
makeMove(worldX, worldY);
return true;
}
return false;
}
bool Board::isGameFinished() const
{
return gameFinished;
}
CellState Board::getWinner() const
{
return winner;
}
bool Board::getCurrentPlayer() const
{
return isCrossTurn;
}
void Board::resetGame()
{
if (!cells) return;
for (int i = 0; i < width; ++i)
{
for (int j = 0; j < height; ++j)
{
cells[i][j].setState(CellState::Empty);
}
}
isCrossTurn = true;
gameFinished = false;
winner = CellState::Empty;
}
main.cpp
#include <SFML/Graphics.hpp>
#include "board.h"
int main() {
sf::RenderWindow window(sf::VideoMode(800, 600), "5 in a Row - Tic Tac Toe");
Board board;
if (!board.initialize()) {
return -1;
}
sf::Vector2f cameraOffset(0.0f, 0.0f);
bool isDragging = false;
sf::Vector2f lastMousePos;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::MouseButtonPressed) {
if (event.mouseButton.button == sf::Mouse::Left) {
sf::Vector2f mousePos = window.mapPixelToCoords(
sf::Vector2i(event.mouseButton.x, event.mouseButton.y));
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) {
isDragging = true;
lastMousePos = mousePos;
}
else {
board.handleClick(mousePos, cameraOffset);
}
}
}
if (event.type == sf::Event::MouseButtonReleased) {
if (event.mouseButton.button == sf::Mouse::Left) {
isDragging = false;
}
}
if (event.type == sf::Event::MouseMoved && isDragging) {
sf::Vector2f mousePos = window.mapPixelToCoords(
sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
sf::Vector2f delta = mousePos - lastMousePos;
cameraOffset += delta;
lastMousePos = mousePos;
}
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::R) {
board.resetGame();
}
}
}
board.updateVisibleArea(cameraOffset, window.getSize());
window.clear(sf::Color::White);
board.draw(window, cameraOffset);
window.display();
}
return 0;
}