Working on Android version

This commit is contained in:
Vladislav Khorev committed 2026-07-18 13:19:08 +03:00
1 parent 0975f3b9cd
commit e773b51985
19 files changed
+418 -264

No files matched your search

+24 -13
View File
@@ -22,7 +22,7 @@ AudioPlayerAsync::~AudioPlayerAsync() {
}
bool AudioPlayerAsync::init() {
if (initialized) return true;
/*if (initialized) return true;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
std::cerr << "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: " << SDL_GetError() << std::endl;
@@ -37,13 +37,13 @@ bool AudioPlayerAsync::init() {
Mix_AllocateChannels(16);
initialized = true;
FRG::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;
FRG::logger() << "AudioPlayerAsync initialized with SDL2_mixer" << std::endl;*/
return true;
}
void AudioPlayerAsync::shutdown() {
if (!initialized) return;
/*
if (currentMusic_) {
Mix_HaltMusic();
Mix_FreeMusic(currentMusic_);
@@ -61,11 +61,11 @@ void AudioPlayerAsync::shutdown() {
Mix_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
initialized = false;
FRG::logger() << "AudioPlayerAsync shutdown" << std::endl;
FRG::logger() << "AudioPlayerAsync shutdown" << std::endl;*/
}
void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, int channel) {
if (!initialized) {
/*if (!initialized) {
std::cerr << "AudioPlayerAsync not initialized" << std::endl;
return;
}
@@ -104,10 +104,11 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
taskQueue.push(std::move(task));
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
if (!initialized) return;
/*if (!initialized) return;
if (filePath == currentTrack)
return;
@@ -141,10 +142,11 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
taskQueue.push(std::move(task));
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::stopMusicAsync() {
if (!initialized) return;
/*if (!initialized) return;
currentTrack = "";
@@ -157,10 +159,11 @@ void AudioPlayerAsync::stopMusicAsync() {
});
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::pauseMusicAsync() {
if (!initialized) return;
/*if (!initialized) return;
#ifdef __EMSCRIPTEN__
Mix_PauseMusic();
#else
@@ -170,10 +173,11 @@ void AudioPlayerAsync::pauseMusicAsync() {
});
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::resumeMusicAsync() {
if (!initialized) return;
/*if (!initialized) return;
#ifdef __EMSCRIPTEN__
Mix_ResumeMusic();
#else
@@ -183,9 +187,11 @@ void AudioPlayerAsync::resumeMusicAsync() {
});
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::setMusicVolume(int volume) {
/*
if (!initialized) return;
volume = std::max(0, std::min(128, volume));
musicVolume_ = volume;
@@ -198,9 +204,11 @@ void AudioPlayerAsync::setMusicVolume(int volume) {
});
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::setSoundVolume(int volume) {
/*
if (!initialized) return;
volume = std::max(0, std::min(128, volume));
soundVolume_ = volume;
@@ -213,10 +221,11 @@ void AudioPlayerAsync::setSoundVolume(int volume) {
});
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs, int loops) {
if (!initialized) return;
/*if (!initialized) return;
if (filePath == currentTrack) return;
currentTrack = filePath;
if (!musicEnabled_) return;
@@ -253,10 +262,11 @@ void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fade
taskQueue.push(std::move(task));
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePath, int fadeDurationMs, int loops) {
if (!initialized) return;
/*if (!initialized) return;
if (filePath == currentTrack) return;
currentTrack = filePath;
if (!musicEnabled_) return;
@@ -296,10 +306,11 @@ void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePa
taskQueue.push(std::move(task));
cv.notify_one();
#endif
*/
}
void AudioPlayerAsync::setMusicEnabled(bool enabled) {
if (musicEnabled_ == enabled) return;
/*if (musicEnabled_ == enabled) return;
musicEnabled_ = enabled;
if (!initialized) return;
@@ -319,7 +330,7 @@ void AudioPlayerAsync::setMusicEnabled(bool enabled) {
currentTrack = ""; // clear so crossFadeMusicAsync doesn't bail early
crossFadeMusicAsync(track);
}
}
}*/
}
void AudioPlayerAsync::setSoundEnabled(bool enabled) {
+6 -3
View File
@@ -1,6 +1,8 @@
#pragma once
#if 0
#include <SDL2/SDL_mixer.h>
#endif
#include <string>
#include <unordered_map>
#include <memory>
@@ -51,17 +53,18 @@ private:
std::queue<std::function<void()>> taskQueue;
#endif
bool stop = false;
#if 0
std::unordered_map<std::string, Mix_Chunk*> soundCache;
std::mutex soundCacheMutex;
std::string currentTrack;
Mix_Music* currentMusic_ = nullptr;
Mix_Music* currentMusic_ = nullptr;
#endif
int musicVolume_ = 128;
int soundVolume_ = 128;
bool musicEnabled_ = true;
bool soundEnabled_ = true;
bool initialized = false;
};
};
+3 -1
View File
@@ -528,12 +528,14 @@ namespace FRG
}
else
{
/*
std::ifstream f(fileName, std::ios::binary | std::ios::ate);
if (!f.is_open()) throw std::runtime_error("Failed to open binary file: " + fileName);
std::streamsize fileSize = f.tellg();
f.seekg(0);
fileData.resize(static_cast<size_t>(fileSize));
f.read(fileData.data(), fileSize);
f.read(fileData.data(), fileSize);*/
fileData = readFile(fileName);
}
const char* ptr = fileData.data();
+1 -1
View File
@@ -23,7 +23,7 @@ int Environment::height = CONST_DEFAULT_HEIGHT;
float Environment::zoom = DEFAULT_ZOOM;
bool Environment::enableLogging = false;
bool Environment::enableLogging = true;
SDL_Window* Environment::window = nullptr;
+86 -135
View File
@@ -24,11 +24,16 @@
#include "GameConstants.h"
#ifdef __ANDROID__
extern bool isAppActive;
#endif
namespace FRG
{
static const float zoomMin = 6.0f;
static const float zoomMax = 20.0f;
#ifdef EMSCRIPTEN
const char* CONST_ZIP_FILE = "resources.zip";
#else
@@ -81,8 +86,10 @@ namespace FRG
}
void Game::setup() {
#ifndef __ANDROID__
Environment::width = Environment::CONST_DEFAULT_WIDTH;
Environment::height = Environment::CONST_DEFAULT_HEIGHT;
#endif
Environment::computeProjectionDimensions();
FRG::BindOpenGlFunctions();
@@ -156,7 +163,9 @@ namespace FRG
});
loadSteps.push([this]() {
#if defined(EMSCRIPTEN)
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE);
@@ -181,7 +190,14 @@ namespace FRG
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
/*#elif defined(__linux__)
renderer.shaderManager.AddShaderFromFiles("text2d",
"resources/shaders/text2d.vertex",
"resources/shaders/text2d_web.fragment",
CONST_ZIP_FILE
);
/*#elif defined(__linux__)
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_desktop.fragment", CONST_ZIP_FILE);
@@ -233,6 +249,12 @@ namespace FRG
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("text2d",
"resources/shaders/text2d.vertex",
"resources/shaders/text2d_desktop.fragment",
CONST_ZIP_FILE
);
#endif
});
@@ -1175,6 +1197,23 @@ namespace FRG
Environment::exitGameLoop = true;
}
#ifdef __ANDROID__
if (event.type == SDL_APP_WILLENTERBACKGROUND) {
isAppActive = false;
__android_log_print(ANDROID_LOG_INFO, "Game", "App moving to background, suspending loop");
break;
}
if (event.type == SDL_APP_DIDENTERFOREGROUND) {
isAppActive = true;
__android_log_print(ANDROID_LOG_INFO, "Game", "App returned to foreground, resuming loop");
break;
}
/*
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::update step 1 event type: %d", event.type);
*/
#endif
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
const bool wasPortrait = Environment::width < Environment::height;
@@ -1211,7 +1250,12 @@ namespace FRG
// Suppress all input while portrait overlay is shown
//if (mobileIsPortrait) continue;
/*
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::update step 2 event type: %d", event.type);
#endif
*/
// Ignore pointer input until the initial loading screen has finished.
// Otherwise a click on the loading screen (e.g. at the spot where a
// main-menu button will later be) gets delivered to the menu UI as
@@ -1222,6 +1266,12 @@ namespace FRG
continue;
}
/*
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::update step 3 event type: %d", event.type);
#endif
*/
#ifdef __ANDROID__
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
Environment::exitGameLoop = true;
@@ -1232,6 +1282,12 @@ namespace FRG
// SDL_HINT_TOUCH_MOUSE_EVENTS="0" is set in main.cpp so these do not
// also fire SDL_MOUSEBUTTONDOWN, preventing double-processing.
if (event.type == SDL_FINGERDOWN || event.type == SDL_FINGERUP || event.type == SDL_FINGERMOTION) {
/*
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::update step 4 event type: %d", event.type);
#endif
*/
int eventX = static_cast<int>(event.tfinger.x * Environment::width);
int eventY = static_cast<int>(event.tfinger.y * Environment::height);
int mx = static_cast<int>(event.tfinger.x * Environment::projectionWidth);
@@ -1249,7 +1305,12 @@ namespace FRG
}
continue;
}
/*
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::update step 5 event type: %d", event.type);
#endif
*/
// Mouse-left maps to a single virtual touch with MOUSE_FINGER_ID.
// Right mouse is intentionally unused now — camera rotation is on hold-and-drag.
if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
@@ -1304,136 +1365,6 @@ namespace FRG
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
switch (event.key.keysym.sym) {
/*case SDLK_8:
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
currentLocation()->editor.selectInteractiveObject(8);
} else {
gameState.isDawn = !gameState.isDawn;
if (gameState.isDawn) gameState.isNight = true;
}
break;
case SDLK_9:
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
currentLocation()->editor.selectInteractiveObject(9);
} else {
startNightTransition();
}
break;
case SDLK_0:
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
}
else {
currentLocation()->requestDarklandsTransition();
}
break;
case SDLK_1:
case SDLK_2:
case SDLK_3:
case SDLK_4:
case SDLK_5:
case SDLK_6:
case SDLK_7:
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
} else {
currentLocation()->switchNavigation(event.key.keysym.sym - SDLK_0);
logger() << "Switched to nav mesh " << (event.key.keysym.sym - SDLK_0) << std::endl;
}
break;
case SDLK_f:
currentLocation()->dialogueSystem.startDialogue("phone_night_aiperi001");
break;
case SDLK_e:
currentLocation()->dialogueSystem.startCutscene("computer_cutscene001"); //.startDialogue("test_cutscene_pan_dialogue");
break;
case SDLK_n:
if (editorMode == EditorMode::None)
editorMode = EditorMode::Navigation;
else if (editorMode == EditorMode::Navigation)
editorMode = EditorMode::InteractiveObjects;
else
editorMode = EditorMode::None;
if (currentLocation()) {
currentLocation()->editorMode = editorMode;
if (editorMode == EditorMode::Navigation)
currentLocation()->editor.buildNavMeshes();
else if (editorMode == EditorMode::InteractiveObjects)
currentLocation()->editor.buildInteractiveObjectBoundsMeshes();
}
{
const char* modeName = (editorMode == EditorMode::Navigation) ? "Navigation"
: (editorMode == EditorMode::InteractiveObjects) ? "InteractiveObjects"
: "None";
logger() << "[EDITOR] Mode: " << modeName << std::endl;
}
break;
case SDLK_o:
x = x + 0.05;
logger() << "current x: " << x << std::endl;
//y = y + 0.002;
//currentLocation->player->hp = 200;
//currentLocation->npcs[0]->walkSpeed += 0.01f;
//logger() << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
break;
case SDLK_k:
x = x - 0.05;
logger() << "current x: " << x << std::endl;
logger() << "Player pos: " << currentLocation()->player->state.position.transpose() << std::endl;
logger() << "Player rotation: " << currentLocation()->player->state.facingAngle*180.0/M_PI << std::endl;
//currentLocation->npcs[0]->walkSpeed -= 0.01f;
//logger() << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
break;
case SDLK_p:
gameState.currentLocationName = "uni_interior";
currentLocation()->player->state.position = Eigen::Vector3f(-0.0189243, 0, -13.4314);
currentLocation()->player->setTarget(currentLocation()->player->state.position);
//logger() << "Switched to location " << ((currentLocation == locations["location1"]) ? "1" : "2") << std::endl;
break;
case SDLK_l:
//x = x - 1;
//logger() << "current x: " << x << std::endl;
logger() << "Azimuth: " << currentLocation()->state.cameraAzimuth << std::endl;
logger() << "Inclination: " << currentLocation()->state.cameraInclination << std::endl;
break;
case SDLK_c:
logger() << "SLOW-MO activated!" << std::endl;
activateSlowMoEffect();
break;
case SDLK_i:
y = y - 1;
logger() << "current y: " << y << std::endl;
break;
case SDLK_b:
if (editorMode != EditorMode::None && currentLocation()) {
currentLocation()->editor.saveAll();
}
break;
case SDLK_j:
currentLocation()->editor.placeTree();
break;
case SDLK_v:
if (editorMode != EditorMode::None && currentLocation()) {
currentLocation()->editor.saveObjects();
}
break;
case SDLK_RETURN:*/
default:
break;
}
@@ -1586,21 +1517,41 @@ namespace FRG
const int uiX = mx;
const int uiY = Environment::projectionHeight - my;
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 1");
#endif
menuManager.topUiManager.onTouchDown(fingerId, uiX, uiY);
const bool capturedByTopUi = menuManager.topUiManager.isUiInteractionForFinger(fingerId);
const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive();
if (!dialogueActive && !capturedByTopUi) {
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 2");
#endif
menuManager.uiManager.onTouchDown(fingerId, uiX, uiY);
}
st.capturedByUi = capturedByTopUi || (!dialogueActive && menuManager.uiManager.isUiInteractionForFinger(fingerId));
activePointers[fingerId] = st;
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 3");
#endif
if (st.capturedByUi) {
// UI button / slider / text-field grabbed this press; don't drive
// gameplay or pinch from it.
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 4");
#endif
return;
}
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_ERROR, "Game",
"Game::onPointerDown step 5");
#endif
if (countNonUiPointers() >= 2) {
// Second finger landed on the gameplay area: switch to pinch-zoom
+35 -3
View File
@@ -861,7 +861,39 @@ namespace FRG {
}
});
lua.script_file(scriptPath);
//lua.script_file(scriptPath);
#ifdef __ANDROID__
// Читаем файл в строку через SDL_RWops
std::string scriptContent = readTextFile(scriptPath);
if (scriptContent.empty()) {
std::cerr << "[script] Failed to read script file or file is empty: " << scriptPath << "\n";
}
else {
// Загружаем (компилируем) строку скрипта. Вторым аргументом передаем имя для логов
auto loadResult = lua.load(scriptContent, scriptPath);
if (!loadResult.valid()) {
sol::error err = loadResult;
std::cerr << "[script] Lua compile error: " << err.what() << "\n";
}
else {
// Если компиляция успешна — вызываем функцию для выполнения кода скрипта
auto runResult = loadResult();
if (!runResult.valid()) {
sol::error err = runResult;
std::cerr << "[script] Lua runtime error: " << err.what() << "\n";
}
}
}
#else
// На остальных платформах оставляем стандартную загрузку из файла
auto result = lua.script_file(scriptPath, sol::script_pass_on_error);
if (!result.valid()) {
sol::error err = result;
std::cerr << "[script] Lua load error: " << err.what() << "\n";
}
#endif
}
void ScriptEngine::callNpcInteractCallback(int npcIndex) {
@@ -885,14 +917,14 @@ namespace FRG {
std::cerr << "[SCRIPT] Lua function 'on_npc_interact' not found!" << std::endl;
}
}
/*
void ScriptEngine::runScript(const std::string& path) {
auto result = impl->lua.safe_script_file(path, sol::script_pass_on_error);
if (!result.valid()) {
sol::error err = result;
std::cerr << "[script] Error in " << path << ": " << err.what() << "\n";
}
}
}*/
void ScriptEngine::callActivateFunction(const std::string& functionName) {
if (!impl) {
+1 -1
View File
@@ -22,7 +22,7 @@ public:
void init(Location* game, Inventory* inventory, AudioPlayerAsync* audioPlayer, const std::string& scriptPath);
// Execute a Lua script file. Logs errors to stderr.
void runScript(const std::string& path);
//void runScript(const std::string& path);
void callItemPickupCallback(const std::string& objectName);
+108 -29
View File
@@ -117,6 +117,8 @@ int main(int argc, char* argv[]) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
FRG::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
glContext_x = SDL_GL_CreateContext(FRG::Environment::window);
@@ -149,40 +151,61 @@ int main(int argc, char* argv[]) {
#ifdef __ANDROID__
extern "C" int SDL_main(int argc, char* argv[]) {
// Инициализация SDL перед получением информации о дисплее
#include <jni.h>
#include <android/log.h>
void Android_InitTouchDevices() {
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
if (!env) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "[Android] Failed to get JNIEnv for touch init");
return;
}
jclass activityClass = env->FindClass("org/libsdl/app/SDLActivity");
if (!activityClass) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "[Android] Failed to find SDLActivity class");
return;
}
jmethodID initTouchMethod = env->GetStaticMethodID(activityClass, "initTouch", "()V");
if (!initTouchMethod) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "[Android] Failed to find initTouch method");
return;
}
env->CallStaticVoidMethod(activityClass, initTouchMethod);
__android_log_print(ANDROID_LOG_INFO, "Game", "[Android] JNI initTouch() successfully invoked");
}
// Переменная для отслеживания фокуса приложения на Android
bool isAppActive = true;
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
return 1;
}
SDL_DisplayMode displayMode;
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
SDL_Quit();
return 1;
}
FRG::Environment::width = displayMode.w;
FRG::Environment::height = displayMode.h;
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
FRG::Environment::width, FRG::Environment::height);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
// Передаем 0, 0 — на Android SDL автоматически растянет окно
// на весь экран с учетом системной ориентации Активити
FRG::Environment::window = SDL_CreateWindow(
"Shadow Over Bishkek",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
FRG::Environment::width, FRG::Environment::height,
0, 0,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
);
@@ -208,32 +231,87 @@ extern "C" int SDL_main(int argc, char* argv[]) {
return 1;
}
// Настройка VSync
// СТРОГО ТУТ: Запрашиваем у созданного окна реальный размер буфера
// Это исключит баг перевернутых координат ширины и высоты
int realWidth, realHeight;
SDL_GL_GetDrawableSize(FRG::Environment::window, &realWidth, &realHeight);
FRG::Environment::width = realWidth;
FRG::Environment::height = realHeight;
__android_log_print(ANDROID_LOG_INFO, "Game", "Actual Render Resolution: %dx%d",
FRG::Environment::width, FRG::Environment::height);
// Теперь, когда окно гарантированно создано в правильной ориентации,
// привязываем тач-устройства на стороне Java
Android_InitTouchDevices();
if (SDL_GL_SetSwapInterval(1) < 0) {
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
}
// Настройка игры
try {
game.setup();
g_game = new FRG::Game();
g_game->setup();
}
catch (const std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
if (g_game) {
delete g_game;
g_game = nullptr;
}
SDL_GL_DeleteContext(ctx);
SDL_DestroyWindow(FRG::Environment::window);
SDL_Quit();
return 1;
}
while (!game.shouldExit()) {
game.update();
SDL_Delay(16);
while (!g_game->shouldExit()) {
SDL_Event event;
// Обрабатываем системные события Android до обновления кадра
/*while (SDL_PollEvent(&event)) {
// Перенаправляем событие в игру, чтобы сработал ваш внутренний цикл (память, ресайз и т.д.)
// Примечание: внутри Game::update у вас тоже стоит PollEvent.
// Чтобы события не терялись, лучше обрабатывать их в одном месте,
// но жизненно важные для ОС флаги перехватываем здесь:
if (event.type == SDL_QUIT) {
break;
}
// Сворачивание / Разворачивание приложения в Android
if (event.type == SDL_APP_WILLENTERBACKGROUND) {
isAppActive = false;
__android_log_print(ANDROID_LOG_INFO, "Game", "App moving to background, suspending loop");
}
if (event.type == SDL_APP_DIDENTERFOREGROUND) {
isAppActive = true;
__android_log_print(ANDROID_LOG_INFO, "Game", "App returned to foreground, resuming loop");
}
}*/
if (isAppActive) {
g_game->update();
// На Android шаг кадров завязан на VSync (SwapInterval = 1).
// SDL_Delay(16) здесь не нужен, он будет вызывать микро-фризы (stuttering).
}
else {
// Если игра свернута — спим, чтобы не нагружать CPU/GPU
SDL_Delay(100);
}
}
delete g_game;
g_game = nullptr;
SDL_GL_DeleteContext(ctx);
SDL_DestroyWindow(FRG::Environment::window);
SDL_Quit();
return 0;
}
#endif
#ifndef __ANDROID__
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__linux__)
// Read the persisted fullscreen preference (if any) before creating the
@@ -417,3 +495,4 @@ int main(int argc, char* argv[]) {
return 0;
}
#endif
#endif
+2 -2
View File
@@ -12,8 +12,8 @@
#ifdef __ANDROID__
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#else
#ifdef __linux__
#include <GL/gl.h>
+3 -3
View File
@@ -87,13 +87,13 @@ namespace FRG {
GL_TEXTURE_2D, depthTexture, 0);
// No color buffer for this FBO — depth only.
#ifdef EMSCRIPTEN
//#ifdef EMSCRIPTEN
GLenum drawBuffers[] = { GL_NONE };
glDrawBuffers(1, drawBuffers);
#else
/*#else
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
#endif
#endif*/
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
+2 -21
View File
@@ -1,6 +1,8 @@
#include "render/TextRenderer.h"
#include <ft2build.h>
#include FT_FREETYPE_H
//#include "freetype/freetype.h"
#include "Environment.h"
#include "render/OpenGlExtensions.h"
@@ -50,27 +52,6 @@ bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixe
r = &renderer;
static bool shaderAlreadyAdded = false;
if (shaderAlreadyAdded == false)
{
shaderAlreadyAdded = true;
#ifdef EMSCRIPTEN
r->shaderManager.AddShaderFromFiles(shaderName,
"resources/shaders/text2d.vertex",
"resources/shaders/text2d_web.fragment",
zipfilename
);
#else
r->shaderManager.AddShaderFromFiles(shaderName,
"resources/shaders/text2d.vertex",
"resources/shaders/text2d_desktop.fragment",
zipfilename
);
#endif
}
FRG::CheckGlError(__FILE__, __LINE__);
if (!loadGlyphs(ttfPath, pixelSize, zipfilename)) return false;
FRG::CheckGlError(__FILE__, __LINE__);
+1 -1
View File
@@ -49,7 +49,7 @@ private:
VertexRenderStruct textMesh;
std::string shaderName = "text2d";
const std::string shaderName = "text2d";
// caching for static texts
struct CachedText {