/
Kotopeska
/
RiseUpGame
Обзор
Документация
Войти
/
Kotopeska
/
RiseUpGame
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
model/GameModel.cpp
340 строк
10 KB
Kotopeska
init
18 май 2025, 19:03
18 май 2025, 19:03
668dfef
Код
Авторство
О чём код?
#include "GameModel.h" #include <chrono> #include <algorithm> #include <cmath> #include <FL/Fl.H> typedef unsigned char uchar; namespace Model { GameModel::GameModel(int width, int height) : m_state(GameState::MENU), m_score(0), m_highScore(0), m_frameCounter(0), m_nextObstacleSpawn(MIN_OBSTACLE_INTERVAL / 4), m_screenWidth(width), m_screenHeight(height), m_scrollSpeed(1.0f) { unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); m_rng = std::mt19937(seed); m_background = std::make_unique<Background>(width, height); float ballRadius = 20.0f; float ballX = width / 2.0f - ballRadius; float ballY = height / 2.0f - ballRadius; m_ball = std::make_unique<Ball>(ballX, ballY, ballRadius); float shieldSize = 30.0f; float shieldX = width / 2.0f - shieldSize / 2.0f; float shieldY = height - shieldSize - 20.0f; m_shield = std::make_unique<Shield>(shieldX, shieldY, shieldSize, shieldSize); } GameModel::~GameModel() { } void GameModel::update() { m_background->update(); if (m_state == GameState::PLAYING) { m_score += 1; m_shield->update(); for (auto &obstacle : m_obstacles) { obstacle.setVelocityY(obstacle.getVelocityY() + m_scrollSpeed * 0.1f); obstacle.update(); } auto initialSize = m_obstacles.size(); m_obstacles.erase( std::remove_if( m_obstacles.begin(), m_obstacles.end(), [this](const Obstacle &obstacle) { return obstacle.isOffScreen(m_screenWidth, m_screenHeight); }), m_obstacles.end()); m_frameCounter++; if (m_frameCounter >= m_nextObstacleSpawn) { int numObstacles = 1 + (m_score / 1000); numObstacles = std::min(numObstacles, 3); for (int i = 0; i < numObstacles; i++) { spawnObstacle(); } m_frameCounter = 0; std::uniform_int_distribution<int> intervalDist( MIN_OBSTACLE_INTERVAL / 3, MAX_OBSTACLE_INTERVAL / 3); int baseInterval = intervalDist(m_rng); std::uniform_int_distribution<int> variationDist(-OBSTACLE_INTERVAL_VARIATION / 2, OBSTACLE_INTERVAL_VARIATION / 2); int variation = variationDist(m_rng); m_nextObstacleSpawn = std::max(MIN_OBSTACLE_INTERVAL / 3, std::min(MAX_OBSTACLE_INTERVAL / 3, baseInterval + variation)); } if (checkCollisions()) { gameOver(); return; } } else if (m_state == GameState::MENU || m_state == GameState::ABOUT) { m_background->update(); } notifyObservers(); } void GameModel::dumpGameState() { } void GameModel::spawnObstacle() { Obstacle obstacle = Obstacle::createRandom(m_screenWidth, m_screenHeight, m_rng); float ballCenterX = m_ball->getX() + m_ball->getRadius(); float ballCenterY = m_ball->getY() + m_ball->getRadius(); float obstacleCenterX = obstacle.getX() + obstacle.getWidth() / 2.0f; float obstacleCenterY = obstacle.getY() + obstacle.getHeight() / 2.0f; float dx = obstacleCenterX - ballCenterX; float dy = obstacleCenterY - ballCenterY; float distance = std::sqrt(dx * dx + dy * dy); float safeDistance = m_ball->getRadius() + obstacle.getWidth() / 2.0f + 20.0f; if (distance < safeDistance) { return; } m_obstacles.push_back(obstacle); } bool GameModel::checkCollisions() { const float ballCenterX = m_ball->getX() + m_ball->getRadius(); const float ballCenterY = m_ball->getY() + m_ball->getRadius(); const float ballRadius = m_ball->getRadius(); for (auto &obstacle : m_obstacles) { const float obstacleCenterX = obstacle.getX() + obstacle.getWidth() / 2.0f; const float obstacleCenterY = obstacle.getY() + obstacle.getHeight() / 2.0f; const float obstacleHalfWidth = obstacle.getWidth() / 2.0f; const float obstacleHalfHeight = obstacle.getHeight() / 2.0f; bool collision = false; switch (obstacle.getType()) { case ObstacleType::CIRCLE: { float dx = obstacleCenterX - ballCenterX; float dy = obstacleCenterY - ballCenterY; float distance = std::sqrt(dx * dx + dy * dy); if (distance < (ballRadius + obstacleHalfWidth)) { collision = true; } } break; case ObstacleType::RECTANGLE: case ObstacleType::TRIANGLE: { float closestX = std::max(obstacle.getX(), std::min(ballCenterX, obstacle.getX() + obstacle.getWidth())); float closestY = std::max(obstacle.getY(), std::min(ballCenterY, obstacle.getY() + obstacle.getHeight())); float dx = ballCenterX - closestX; float dy = ballCenterY - closestY; float distance = std::sqrt(dx * dx + dy * dy); if (distance < ballRadius) { collision = true; } } break; } if (collision) { return true; } if (m_shield->isActive() && m_shield->checkCollision(obstacle)) { float shieldCenterX = m_shield->getX() + m_shield->getWidth() / 2.0f; float shieldCenterY = m_shield->getY() + m_shield->getHeight() / 2.0f; float dx = obstacleCenterX - shieldCenterX; float dy = obstacleCenterY - shieldCenterY; float length = std::sqrt(dx * dx + dy * dy); if (length > 0) { dx /= length; dy /= length; } float forceMagnitude = 10.0f; obstacle.applyForce(dx * forceMagnitude, dy * forceMagnitude); } } return false; } void GameModel::resetGame() { m_score = 0; m_frameCounter = 0; m_nextObstacleSpawn = MIN_OBSTACLE_INTERVAL / 3; m_obstacles.clear(); float ballX = m_screenWidth / 2.0f - m_ball->getRadius(); float ballY = m_screenHeight / 2.0f - m_ball->getRadius(); m_ball->reset(ballX, ballY); float shieldSize = 30.0f; float shieldX = m_screenWidth / 2.0f - shieldSize / 2.0f; float shieldY = m_screenHeight - shieldSize - 20.0f; m_shield->setWidth(shieldSize); m_shield->setHeight(shieldSize); m_shield->setX(shieldX); m_shield->setY(shieldY); m_shield->setActive(false); notifyObservers(); } void GameModel::resize(int width, int height) { m_screenWidth = width; m_screenHeight = height; m_background = std::make_unique<Background>(width, height); float ballX = width / 2.0f - m_ball->getRadius(); float ballY = height / 2.0f - m_ball->getRadius(); m_ball->setX(ballX); m_ball->setY(ballY); float shieldX = width / 2.0f - m_shield->getWidth() / 2.0f; float shieldY = height - m_shield->getHeight() - 20.0f; m_shield->setX(shieldX); m_shield->setY(shieldY); m_obstacles.clear(); notifyObservers(); } void GameModel::handleKeyPress(int key) { if (key == 32) { switch (m_state) { case GameState::MENU: startGame(); break; case GameState::GAME_OVER: showMenu(); break; case GameState::ABOUT: showMenu(); break; default: break; } } notifyObservers(); } void GameModel::handleMouseDown(int x, int y) { if (m_state == GameState::PLAYING) { m_shield->setActive(true); m_shield->setTarget(x - m_shield->getWidth() / 2.0f, y - m_shield->getHeight() / 2.0f); } notifyObservers(); } void GameModel::handleMouseUp(int x, int y) { if (m_state == GameState::PLAYING) { m_shield->setActive(false); } notifyObservers(); } void GameModel::handleMouseMove(int x, int y) { if (m_state == GameState::PLAYING && m_shield->isActive()) { m_shield->setTarget(x - m_shield->getWidth() / 2.0f, y - m_shield->getHeight() / 2.0f); } } void GameModel::startGame() { m_state = GameState::PLAYING; resetGame(); notifyObservers(); } void GameModel::gameOver() { m_state = GameState::GAME_OVER; if (m_score > m_highScore) { m_highScore = m_score; } notifyObservers(); } void GameModel::showMenu() { m_state = GameState::MENU; notifyObservers(); } void GameModel::showAbout() { m_state = GameState::ABOUT; notifyObservers(); } } // namespace Model