Initial commit

This commit is contained in:
2026-06-16 11:01:59 +03:00
commit 35b3c98e62
39 changed files with 1595 additions and 0 deletions
@@ -0,0 +1,237 @@
package ru.samsung.gamestudiovtim2026;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
public class GameScreen implements Screen {
private final MyGdxGame game;
private OrthographicCamera camera;
private SpriteBatch batch;
private BitmapFont font;
private Player player;
private Array<Obstacle> obstacles;
private float obstacleTimer;
private float gameTime;
private float distance;
private float gameSpeed;
private int score;
private int highScore;
private Texture lineTexture;
public GameScreen(MyGdxGame game) {
this.game = game;
this.batch = game.batch;
this.font = game.font;
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
highScore = ScoreManager.loadHighScore();
lineTexture = createLineTexture();
restart();
}
private Texture createLineTexture() {
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.GRAY);
pixmap.fill();
Texture texture = new Texture(pixmap);
pixmap.dispose();
return texture;
}
public void restart() {
player = new Player();
obstacles = new Array<>();
obstacleTimer = 0;
gameTime = 0;
distance = 0;
gameSpeed = 200;
score = 0;
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
update(delta);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
drawLines();
player.render(batch);
for (Obstacle obstacle : obstacles) {
if (obstacle.isVisible()) {
obstacle.render(batch);
}
}
drawUI();
batch.end();
}
private void update(float delta) {
gameTime += delta;
distance += gameSpeed * delta;
player.update(delta);
handleInput();
generateObstacles(delta);
updateObstacles(delta);
checkCollisions();
// Плавное увеличение скорости со временем
gameSpeed += delta * 1.5f;
score = (int)(distance / 10);
}
private void handleInput() {
if (Gdx.input.justTouched()) {
int touchY = Gdx.input.getY();
int screenHeight = Gdx.graphics.getHeight();
if (touchY < screenHeight / 3) {
player.moveUp();
} else if (touchY > 2 * screenHeight / 3) {
player.moveDown();
}
}
if (Gdx.input.isKeyJustPressed(com.badlogic.gdx.Input.Keys.UP) ||
Gdx.input.isKeyJustPressed(com.badlogic.gdx.Input.Keys.W)) {
player.moveUp();
}
if (Gdx.input.isKeyJustPressed(com.badlogic.gdx.Input.Keys.DOWN) ||
Gdx.input.isKeyJustPressed(com.badlogic.gdx.Input.Keys.S)) {
player.moveDown();
}
if (Gdx.input.isKeyJustPressed(com.badlogic.gdx.Input.Keys.SPACE)) {
player.jump();
}
// Для тестирования на ПК - выход по ESC
if (Gdx.input.isKeyJustPressed(com.badlogic.gdx.Input.Keys.ESCAPE)) {
game.switchToMainMenu();
}
}
private void generateObstacles(float delta) {
obstacleTimer += delta;
// Генерируем препятствия с интервалом (интервал уменьшается с увеличением скорости)
float spawnInterval = Math.max(0.8f, 2.0f - (gameSpeed / 500));
if (obstacleTimer > spawnInterval) {
obstacleTimer = 0;
int line = MathUtils.random(0, 2);
float spawnX = 800;
// Проверяем, чтобы препятствия не были слишком близко
boolean tooClose = false;
float minDistance = 150 + (gameSpeed / 10); // Минимальное расстояние увеличивается со скоростью
for (Obstacle obstacle : obstacles) {
if (obstacle.getLine() == line && obstacle.getBounds().x > 800 - minDistance) {
tooClose = true;
break;
}
}
if (!tooClose) {
obstacles.add(new Obstacle(spawnX, line, gameSpeed * 1.2f));
}
}
}
private void updateObstacles(float delta) {
for (int i = obstacles.size - 1; i >= 0; i--) {
Obstacle obstacle = obstacles.get(i);
obstacle.update(delta);
if (!obstacle.isPassed() && obstacle.getBounds().x < player.getBounds().x) {
obstacle.setPassed(true);
score += 5; // Бонус за прохождение препятствия
}
if (obstacle.isOffScreen()) {
obstacles.removeIndex(i);
obstacle.dispose();
}
}
}
private void checkCollisions() {
for (Obstacle obstacle : obstacles) {
if (obstacle.isVisible() && obstacle.collidesWith(player.getBounds())) {
if (score > highScore) {
highScore = score;
ScoreManager.saveHighScore(highScore);
}
ScoreManager.saveLastScore(score);
game.switchToMainMenu();
return;
}
}
}
private void drawLines() {
batch.setColor(Color.GRAY);
for (int i = 0; i < 3; i++) {
float y = 400 - i * 160;
batch.draw(lineTexture, 0, y, 800, 2);
}
batch.setColor(Color.WHITE);
}
private void drawUI() {
font.setColor(Color.WHITE);
font.draw(batch, "Score: " + score, 20, 460);
font.draw(batch, "Best: " + highScore, 20, 440);
// Убрали Distance и Speed из отображения
}
@Override
public void resize(int width, int height) {
camera.setToOrtho(false, 800, 480);
}
@Override
public void show() {}
@Override
public void hide() {}
@Override
public void pause() {}
@Override
public void resume() {}
@Override
public void dispose() {
player.dispose();
for (Obstacle obstacle : obstacles) {
obstacle.dispose();
}
lineTexture.dispose();
}
}
@@ -0,0 +1,150 @@
package ru.samsung.gamestudiovtim2026;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
public class MainMenuScreen implements Screen {
private final MyGdxGame game;
private Stage stage;
private Skin skin;
private OrthographicCamera camera;
private SpriteBatch batch;
private BitmapFont font;
private Label highScoreLabel;
private Label lastScoreLabel;
public MainMenuScreen(MyGdxGame game) {
this.game = game;
this.batch = game.batch;
this.font = game.font;
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
stage = new Stage(new ScreenViewport());
Gdx.input.setInputProcessor(stage);
skin = new Skin();
skin.add("default-font", font);
com.badlogic.gdx.graphics.Pixmap pixmap = new com.badlogic.gdx.graphics.Pixmap(1, 1,
com.badlogic.gdx.graphics.Pixmap.Format.RGBA8888);
pixmap.setColor(Color.WHITE);
pixmap.fill();
skin.add("white", new com.badlogic.gdx.graphics.Texture(pixmap));
pixmap.dispose();
TextButton.TextButtonStyle buttonStyle = new TextButton.TextButtonStyle();
buttonStyle.font = font;
buttonStyle.fontColor = Color.WHITE;
buttonStyle.downFontColor = Color.RED;
buttonStyle.up = skin.newDrawable("white", Color.DARK_GRAY);
buttonStyle.down = skin.newDrawable("white", Color.GRAY);
buttonStyle.over = skin.newDrawable("white", Color.LIGHT_GRAY);
Label.LabelStyle labelStyle = new Label.LabelStyle();
labelStyle.font = font;
labelStyle.fontColor = Color.WHITE;
skin.add("default", buttonStyle);
skin.add("default", labelStyle);
createUI();
}
private void createUI() {
Table table = new Table();
table.setFillParent(true);
stage.addActor(table);
// Английский текст для избежания квадратиков
Label title = new Label("RUNNING SMILEY", skin);
title.setFontScale(1.5f); // Уменьшаем размер заголовка
TextButton playButton = new TextButton("PLAY", skin);
TextButton exitButton = new TextButton("EXIT", skin);
int highScore = ScoreManager.loadHighScore();
int lastScore = ScoreManager.loadLastScore();
highScoreLabel = new Label("BEST: " + highScore, skin);
lastScoreLabel = new Label("LAST: " + lastScore, skin);
playButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
game.switchToGameScreen();
}
});
exitButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
Gdx.app.exit();
}
});
table.add(title).padBottom(40).row();
table.add(playButton).width(180).height(50).padBottom(15).row();
table.add(exitButton).width(180).height(50).padBottom(30).row();
table.add(highScoreLabel).padBottom(10).row();
table.add(lastScoreLabel);
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
// Можно добавить простой фон
batch.end();
stage.act(delta);
stage.draw();
}
@Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
@Override
public void show() {
highScoreLabel.setText("BEST: " + ScoreManager.loadHighScore());
lastScoreLabel.setText("LAST: " + ScoreManager.loadLastScore());
Gdx.input.setInputProcessor(stage);
}
@Override
public void hide() {}
@Override
public void pause() {}
@Override
public void resume() {}
@Override
public void dispose() {
stage.dispose();
skin.dispose();
}
}
@@ -0,0 +1,66 @@
package ru.samsung.gamestudiovtim2026;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class MyGdxGame extends Game {
public static final int SCR_WIDTH = 960;
public static final int SCR_HEIGHT = 960;
public SpriteBatch batch;
public BitmapFont font;
private GameScreen gameScreen;
private MainMenuScreen mainMenuScreen;
@Override
public void create() {
batch = new SpriteBatch();
// Создаем шрифт без FreeType
font = createSimpleFont();
mainMenuScreen = new MainMenuScreen(this);
gameScreen = new GameScreen(this);
setScreen(mainMenuScreen);
}
private BitmapFont createSimpleFont() {
try {
// Пробуем использовать стандартный шрифт, но уменьшим его размер
BitmapFont simpleFont = new BitmapFont();
simpleFont.getData().setScale(0.8f); // Уменьшаем размер шрифта
return simpleFont;
} catch (Exception e) {
// Если что-то пошло не так, создаем минимальный шрифт
return new BitmapFont();
}
}
public void switchToGameScreen() {
gameScreen.restart();
setScreen(gameScreen);
}
public void switchToMainMenu() {
setScreen(mainMenuScreen);
}
@Override
public void render() {
super.render();
}
@Override
public void dispose() {
batch.dispose();
font.dispose();
if (gameScreen != null) {
gameScreen.dispose();
}
if (mainMenuScreen != null) {
mainMenuScreen.dispose();
}
}
}
@@ -0,0 +1,67 @@
package ru.samsung.gamestudiovtim2026;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
public class Obstacle {
private Texture texture;
private Rectangle bounds;
private float speed;
private int line; // Линия, на которой находится препятствие
private boolean passed;
public Obstacle(float x, int line, float speed) {
texture = new Texture("spike_block.png");
this.line = line;
this.speed = speed;
this.passed = false;
float[] linePositions = {400, 240, 80};
bounds = new Rectangle(x, linePositions[line], 60, 60);
}
public void update(float deltaTime) {
// Препятствия движутся справа налево
bounds.x -= speed * deltaTime;
}
public void render(SpriteBatch batch) {
// Отрисовываем в мировых координатах
batch.draw(texture, bounds.x, bounds.y, bounds.width, bounds.height);
}
public boolean isOffScreen() {
// Препятствие ушло за левый край экрана
return bounds.x < -bounds.width;
}
public boolean isVisible() {
// Препятствие видимо на экране (не вышло за левый край)
return bounds.x > -bounds.width;
}
public boolean collidesWith(Rectangle playerBounds) {
return bounds.overlaps(playerBounds);
}
public boolean isPassed() {
return passed;
}
public void setPassed(boolean passed) {
this.passed = passed;
}
public void dispose() {
texture.dispose();
}
public Rectangle getBounds() {
return bounds;
}
public int getLine() {
return line;
}
}
@@ -0,0 +1,79 @@
package ru.samsung.gamestudiovtim2026;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
public class Player {
private Texture texture;
private Rectangle bounds;
private int currentLine; // 0 - верхняя, 1 - средняя, 2 - нижняя
private float[] linePositions = {400, 240, 80}; // Y-позиции линий
private boolean isJumping;
private float jumpTime;
public Player() {
texture = new Texture("smile.png");
// Игрок фиксирован по X (100px от левого края)
bounds = new Rectangle(100, linePositions[1], 60, 60);
currentLine = 1; // Начинаем со средней линии
isJumping = false;
}
public void update(float deltaTime) {
// Игрок НЕ движется по X, только плавно перемещается по Y между линиями
// Плавное перемещение между линиями
float targetY = linePositions[currentLine];
if (Math.abs(bounds.y - targetY) > 1) {
bounds.y += (targetY - bounds.y) * 5 * deltaTime;
}
// Обработка прыжка
if (isJumping) {
jumpTime += deltaTime;
if (jumpTime > 0.3f) { // Длительность прыжка
isJumping = false;
jumpTime = 0;
}
}
}
public void moveUp() {
if (currentLine > 0) {
currentLine--;
}
}
public void moveDown() {
if (currentLine < 2) {
currentLine++;
}
}
public void jump() {
if (!isJumping) {
isJumping = true;
}
}
public void render(SpriteBatch batch) {
float drawY = bounds.y;
if (isJumping) {
drawY += 50 * (float)Math.sin(jumpTime * 10);
}
batch.draw(texture, bounds.x, drawY, bounds.width, bounds.height);
}
public Rectangle getBounds() {
return bounds;
}
public int getCurrentLine() {
return currentLine;
}
public void dispose() {
texture.dispose();
}
}
@@ -0,0 +1,32 @@
package ru.samsung.gamestudiovtim2026;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
public class ScoreManager {
private static final String PREFS_NAME = "game_preferences";
private static final String HIGH_SCORE_KEY = "high_score";
private static final String LAST_SCORE_KEY = "last_score";
public static void saveHighScore(int score) {
Preferences prefs = Gdx.app.getPreferences(PREFS_NAME);
prefs.putInteger(HIGH_SCORE_KEY, score);
prefs.flush();
}
public static int loadHighScore() {
Preferences prefs = Gdx.app.getPreferences(PREFS_NAME);
return prefs.getInteger(HIGH_SCORE_KEY, 0);
}
public static void saveLastScore(int score) {
Preferences prefs = Gdx.app.getPreferences(PREFS_NAME);
prefs.putInteger(LAST_SCORE_KEY, score);
prefs.flush();
}
public static int loadLastScore() {
Preferences prefs = Gdx.app.getPreferences(PREFS_NAME);
return prefs.getInteger(LAST_SCORE_KEY, 0);
}
}