Add mobile support
All checks were successful
Build & deploy web version / Build WebAssembly & deploy (push) Successful in 1m1s
All checks were successful
Build & deploy web version / Build WebAssembly & deploy (push) Successful in 1m1s
This commit is contained in:
parent
6e4f0428dc
commit
c4f7565d18
18 changed files with 567 additions and 69 deletions
|
|
@ -30,6 +30,7 @@ if(EMSCRIPTEN)
|
|||
"SHELL:-sALLOW_MEMORY_GROWTH=1"
|
||||
"SHELL:-sEXIT_RUNTIME=0"
|
||||
"SHELL:-sFORCE_FILESYSTEM=1"
|
||||
"SHELL:-sEXPORTED_RUNTIME_METHODS=ccall"
|
||||
"SHELL:-lidbfs.js"
|
||||
"SHELL:--preload-file ${GAME_SRC}/resources@/resources"
|
||||
"SHELL:--shell-file ${GAME_WEB_SHELL}"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "modules/map.h"
|
||||
#include "modules/menu/menuDraw.h"
|
||||
#include "modules/menu/menuFSM.h"
|
||||
#include "modules/touch.h"
|
||||
|
||||
|
||||
/**\mainpage Leírás
|
||||
|
|
@ -93,6 +94,7 @@ static void handleEvents() {
|
|||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
|
||||
noteMouseInput();
|
||||
if(event.button.button == SDL_BUTTON_LEFT) handleClicks(&prematureClick);
|
||||
if(event.button.button == SDL_BUTTON_RIGHT) isRMBDown = true;
|
||||
if(event.button.button == SDL_BUTTON_LEFT) isLMBDown = true;
|
||||
|
|
@ -106,6 +108,7 @@ static void handleEvents() {
|
|||
|
||||
case SDL_MOUSEMOTION:
|
||||
|
||||
noteMouseInput();
|
||||
setMousePosition(event.motion.x, event.motion.y);
|
||||
break;
|
||||
|
||||
|
|
@ -120,6 +123,13 @@ static void handleEvents() {
|
|||
processTextInput(event.text.text);
|
||||
break;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
case SDL_FINGERMOTION:
|
||||
case SDL_FINGERUP:
|
||||
|
||||
handleFingerEvent(&event, &isLMBDown, &prematureClick);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,11 +154,14 @@ static void frame() {
|
|||
setRenderingEnabled(isLastStep);
|
||||
|
||||
if(isLastStep) SDL_FillRect(screen, NULL, backgroundColor);
|
||||
doStuff(isLMBDown, isRMBDown, prematureClick);
|
||||
applyTouchKeys();
|
||||
doStuff(isLMBDown, isRMBDown || isTouchSlowMo(), prematureClick);
|
||||
hasDrawn = hasDrawn || isLastStep;
|
||||
}
|
||||
setRenderingEnabled(true);
|
||||
|
||||
syncSoftKeyboard(getGameState());
|
||||
|
||||
if(hasDrawn) presentFrame();
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if(getGameState() == quit || quitRequested) emscripten_cancel_main_loop();
|
||||
|
|
@ -160,7 +173,12 @@ static void frame() {
|
|||
int main(int argc, char *argv[]) {
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
//az érintéseket magunk dolgozzuk fel, az sdl egérré alakítása egyszerre csak egy ujjat tudna
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
|
||||
openWindow();
|
||||
initTouch();
|
||||
keys = (unsigned char*) SDL_GetKeyboardState(NULL);
|
||||
SDL_StartTextInput();
|
||||
|
||||
|
|
|
|||
|
|
@ -99,10 +99,12 @@ void handleResize() {
|
|||
|
||||
if(screen != NULL && screen -> w == outputWidth && screen -> h == outputHeight) return;
|
||||
|
||||
bool heightChanged = screen == NULL || screen -> h != outputHeight;
|
||||
//a betűméret a kisebbik képernyőoldalhoz igazodik, csak annak változásakor kell újratölteni
|
||||
bool scaleChanged = screen == NULL ||
|
||||
smaller(screen -> w, screen -> h) != smaller(outputWidth, outputHeight);
|
||||
createDrawingSurface(outputWidth, outputHeight);
|
||||
|
||||
if(heightChanged) {
|
||||
if(scaleChanged) {
|
||||
freeFonts();
|
||||
openFonts();
|
||||
}
|
||||
|
|
@ -218,7 +220,7 @@ mousePos getMousePosition() {
|
|||
|
||||
///Lerajzolja a képernyőre az egeret, erre azért van szükség, mert az SDL csak fekete-fehér egérmutatót kezel.
|
||||
void drawMouse(bool isBW) {
|
||||
Sint16 size = larger(4, mouseSize * screen -> h / designHeight);
|
||||
Sint16 size = larger(4, (int)(mouseSize * uiScale()));
|
||||
Sint16 x1 = mouse.x - size / 2;
|
||||
Sint16 x2 = mouse.x + size / 2;
|
||||
Sint16 y1 = mouse.y - size / 2;
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ int aacircleColor(SDL_Surface* dst, Sint16 x, Sint16 y, Sint16 rad, Uint32 color
|
|||
return 0;
|
||||
}
|
||||
|
||||
int filledPolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
||||
int filledPolygonRGBA(SDL_Surface* dst, const float* vx, const float* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
||||
if(n < 3) return -1;
|
||||
|
||||
target t;
|
||||
|
|
@ -212,26 +212,32 @@ int filledPolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int
|
|||
if(crossings == NULL) { endTarget(&t); return -1; }
|
||||
}
|
||||
|
||||
int minY = vy[0], maxY = vy[0];
|
||||
double minY = vy[0], maxY = vy[0];
|
||||
int i;
|
||||
for(i = 1; i < n; i++) {
|
||||
if(vy[i] < minY) minY = vy[i];
|
||||
if(vy[i] > maxY) maxY = vy[i];
|
||||
}
|
||||
|
||||
int yStart = minY < 0 ? 0 : minY;
|
||||
int yEnd = maxY > t.h - 1 ? t.h - 1 : maxY;
|
||||
//A vágás még a törtszámokon megtörténik, így a kerekítés biztosan nem lóg ki az int-ből
|
||||
if(minY < 0) minY = 0;
|
||||
if(maxY > t.h - 1) maxY = t.h - 1;
|
||||
if(minY > maxY) { if(crossings != inlineCrossings) free(crossings); endTarget(&t); return 0; }
|
||||
|
||||
int yStart = (int) ceil(minY);
|
||||
int yEnd = (int) floor(maxY);
|
||||
|
||||
int y;
|
||||
for(y = yStart; y <= yEnd; y++) {
|
||||
int count = 0;
|
||||
for(i = 0; i < n; i++) {
|
||||
int j = (i + 1) % n;
|
||||
int y1 = vy[i], y2 = vy[j], x1 = vx[i], x2 = vx[j];
|
||||
if(y1 > y2) { int tmp = y1; y1 = y2; y2 = tmp; tmp = x1; x1 = x2; x2 = tmp; }
|
||||
double y1 = vy[i], y2 = vy[j], x1 = vx[i], x2 = vx[j];
|
||||
if(y1 > y2) { double tmp = y1; y1 = y2; y2 = tmp; tmp = x1; x1 = x2; x2 = tmp; }
|
||||
|
||||
if((y >= y1 && y < y2) || (y == maxY && y > y1 && y <= y2))
|
||||
crossings[count++] = x1 + ((double) (y - y1) * (x2 - x1)) / (y2 - y1);
|
||||
//Félig nyílt intervallum, hogy a két él közös csúcsát pontosan egyszer számoljuk
|
||||
if(y >= y1 && y < y2)
|
||||
crossings[count++] = x1 + ((y - y1) * (x2 - x1)) / (y2 - y1);
|
||||
}
|
||||
|
||||
if(count < 2) continue;
|
||||
|
|
@ -244,8 +250,15 @@ int filledPolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int
|
|||
crossings[k + 1] = key;
|
||||
}
|
||||
|
||||
for(i = 0; i + 1 < count; i += 2)
|
||||
blendHLine(&t, (int) floor(crossings[i] + 0.5), (int) floor(crossings[i + 1] + 0.5), y, r, g, b, a);
|
||||
for(i = 0; i + 1 < count; i += 2) {
|
||||
double left = crossings[i], right = crossings[i + 1];
|
||||
if(left < 0) left = 0;
|
||||
if(right > t.w - 1) right = t.w - 1;
|
||||
if(left > right) continue;
|
||||
|
||||
int xa = (int) ceil(left), xb = (int) floor(right);
|
||||
if(xa <= xb) blendHLine(&t, xa, xb, y, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
if(crossings != inlineCrossings) free(crossings);
|
||||
|
|
@ -283,7 +296,7 @@ static void aaLine(const target* t, double x0, double y0, double x1, double y1,
|
|||
}
|
||||
}
|
||||
|
||||
int aapolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
||||
int aapolygonRGBA(SDL_Surface* dst, const float* vx, const float* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
||||
if(n < 2) return -1;
|
||||
|
||||
target t;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
int lineColor(SDL_Surface* dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color);
|
||||
int filledCircleColor(SDL_Surface* dst, Sint16 x, Sint16 y, Sint16 rad, Uint32 color);
|
||||
int aacircleColor(SDL_Surface* dst, Sint16 x, Sint16 y, Sint16 rad, Uint32 color);
|
||||
int filledPolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
|
||||
int aapolygonRGBA(SDL_Surface* dst, const Sint16* vx, const Sint16* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
|
||||
int filledPolygonRGBA(SDL_Surface* dst, const float* vx, const float* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
|
||||
int aapolygonRGBA(SDL_Surface* dst, const float* vx, const float* vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
|
||||
|
||||
#endif // GFXPRIMITIVES_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
#include "unicodePrint.h"
|
||||
|
||||
|
||||
/** \brief A feliratok és a felület méretezéséhez használt arányszám.
|
||||
* A program az 1920x1080-as képernyőre készült, minden mérete ehhez képest értendő.
|
||||
* Csak a magassághoz kötni viszont nem szabad: álló tájolású telefonon a képernyő magas,
|
||||
* de keskeny, így a magasságból számolt betűk kilógnának a szélességből. A kisebbik
|
||||
* képernyőoldalhoz igazítva a szöveg akkora lesz, amekkorára a fekvő képernyőn is, tehát
|
||||
* a készülék elforgatása nem változtatja meg a méretét.
|
||||
*/
|
||||
float uiScale() {
|
||||
if(screen == NULL) return 1;
|
||||
return (float) smaller(screen -> w, screen -> h) / designHeight;
|
||||
}
|
||||
|
||||
///Indításnál betölti a szükséges betűtípusokat.
|
||||
void openFonts() {
|
||||
if(!TTF_WasInit()) TTF_Init();
|
||||
|
||||
float scale = (float) screen -> h / designHeight;
|
||||
float scale = uiScale();
|
||||
fontSmaller = TTF_OpenFont("resources/Roboto-Thin.ttf", larger(1, (int)(40 * scale)));
|
||||
fontSmall = TTF_OpenFont("resources/Roboto-Thin.ttf", larger(1, (int)(48 * scale)));
|
||||
fontSmallBold = TTF_OpenFont("resources/Roboto-Light.ttf", larger(1, (int)(53 * scale)));
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
tiny, small, smallBold, large
|
||||
} fontType;
|
||||
|
||||
float uiScale();
|
||||
void openFonts();
|
||||
void freeFonts();
|
||||
bool isValidChar(wchar_t chr);
|
||||
|
|
|
|||
|
|
@ -155,26 +155,34 @@ bool cameraDollyZ(int deltaTime, bool restart) {
|
|||
* Ez interaktívan ábrázolva így néz ki: https://www.desmos.com/calculator/8qjvkptjmn
|
||||
* \param Térvektor típusú pont
|
||||
* \return Síkvektor típusú pont
|
||||
* A visszaadott pontnak int koordinátái vannak, hiszen valamelyik pixel koordinátáit jelöli.
|
||||
* A visszaadott pont koordinátái törtszámok: egy pixelen belüli helyet is meg tudnak adni.
|
||||
* Erre azért van szükség, mert a kamera folyamatosan mozog, és ha itt pixelre kerekítenénk,
|
||||
* akkor a testek élei egymástól függetlenül, más-más képkockán ugranának át a szomszéd pixelre.
|
||||
* Ez a függvény igazából egy camera obscura modellje.
|
||||
*/
|
||||
intVector2D get2DPoint(vector3D point) {
|
||||
vector2D get2DPoint(vector3D point) {
|
||||
//Ha watchBounds = true, csak egy megadott kockán belül adja vissza az értéket (rajzol), ez a feldarabolás miatt fontos
|
||||
if(watchBounds) {
|
||||
putInsideIntervalF(leftBound, &point.x, rightBound);
|
||||
putInsideIntervalF(lowerBound, &point.y, upperBound);
|
||||
}
|
||||
|
||||
//Ezt a két kifejezést újra felhasználjuk
|
||||
/* A metszéspont képlete kibontva összevonható, hiszen zDistFromHole = zDist - focalLength:
|
||||
* (kamera * (zDist - f) + f * pont) / zDist - kamera == f * (pont - kamera) / zDist
|
||||
* A két alak matematikailag azonos, de a bal oldali a pálya közepén (kamera ~1000 m-nél)
|
||||
* egy ~20000 nagyságrendű számból vonna ki majdnem ugyanakkorát, és a maradék néhány
|
||||
* tizedből épp a lényeg veszne el: a float pontossága ott már csak ezred méter, amit a
|
||||
* magnification több tized pixelnyi hibává nagyít. A jobb oldali alak előbb végzi el a
|
||||
* kivonást (két közeli szám különbsége hibamentes), így a leképezés a pálya szélén is
|
||||
* pixeltört pontos marad. */
|
||||
float zDist = point.z - cameraPosition.z;
|
||||
float zDistFromHole = zDist - focalLength;
|
||||
float dx = (cameraPosition.x * zDistFromHole + focalLength * point.x) / zDist - cameraPosition.x;
|
||||
float dy = (cameraPosition.y * zDistFromHole + focalLength * point.y) / zDist - cameraPosition.y;
|
||||
float dx = focalLength * (point.x - cameraPosition.x) / zDist;
|
||||
float dy = focalLength * (point.y - cameraPosition.y) / zDist;
|
||||
|
||||
//Megfelelően tükrözzük a kapot pontot, és a méterben kapott értéket átváltjuk pixelekre
|
||||
intVector2D resultPoint = {
|
||||
(int)(screen -> w / 2 - magnification * dx),
|
||||
(int)(screen -> h / 2 + magnification * dy)
|
||||
vector2D resultPoint = {
|
||||
screen -> w / 2.0f - magnification * dx,
|
||||
screen -> h / 2.0f + magnification * dy
|
||||
};
|
||||
|
||||
return resultPoint;
|
||||
|
|
@ -190,9 +198,9 @@ intVector2D get2DPoint(vector3D point) {
|
|||
* Ezt egyszer kitöltéssel csinálja, egyszer pedig anti-aliasing poligonnal,
|
||||
* hogy a rajz széle ne legyen recegős.
|
||||
*/
|
||||
static void drawTetragon(intVector2D a, intVector2D b, intVector2D c, intVector2D d, color color) {
|
||||
Sint16 xCoords[4] = {(Sint16)a.x, (Sint16)b.x, (Sint16)c.x, (Sint16)d.x};
|
||||
Sint16 yCoords[4] = {(Sint16)a.y, (Sint16)b.y, (Sint16)c.y, (Sint16)d.y};
|
||||
static void drawTetragon(vector2D a, vector2D b, vector2D c, vector2D d, color color) {
|
||||
float xCoords[4] = {a.x, b.x, c.x, d.x};
|
||||
float yCoords[4] = {a.y, b.y, c.y, d.y};
|
||||
|
||||
filledPolygonRGBA(screen, xCoords, yCoords, 4, color.r, color.g, color.b, color.a);
|
||||
aapolygonRGBA(screen, xCoords, yCoords, 4, color.r, color.g, color.b, color.a);
|
||||
|
|
@ -206,7 +214,7 @@ static void drawTetragon(intVector2D a, intVector2D b, intVector2D c, intVector2
|
|||
* azokat se rajzolja le, amiket a helyzetük miatt biztosan nem láthat a kamera.
|
||||
*/
|
||||
void drawCuboid(renderObject object, bool sides[]) {
|
||||
intVector2D vertexes[8];
|
||||
vector2D vertexes[8];
|
||||
//A téglatestet bejárva megkeressük a csúcsait
|
||||
int i;
|
||||
for(i = 0; i < 8; i++) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
void menuCamera(int* getColumns, int* getRows);
|
||||
int getColumns();
|
||||
bool cameraDollyZ(int deltaTime, bool restart);
|
||||
intVector2D get2DPoint(vector3D point);
|
||||
vector2D get2DPoint(vector3D point);
|
||||
void drawCuboid(renderObject object, bool sides[]);
|
||||
void drawMap(int deltaTime);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ void createProjectile(vector3D playerPosition, vector3D playerSize, vector2D* pl
|
|||
newProjectile -> position.y = playerPosition.y + playerSize.y / 2;
|
||||
newProjectile -> size = projectileSize;
|
||||
|
||||
intVector2D playerCoord = get2DPoint((vector3D) {newProjectile -> position.x, newProjectile -> position.y, 0});
|
||||
int deltaX = mouse.x - playerCoord.x;
|
||||
int deltaY = playerCoord.y - mouse.y;
|
||||
vector2D playerCoord = get2DPoint((vector3D) {newProjectile -> position.x, newProjectile -> position.y, 0});
|
||||
float deltaX = mouse.x - playerCoord.x;
|
||||
float deltaY = playerCoord.y - mouse.y;
|
||||
float distance = sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
float compensation = 0;
|
||||
if(distance > 0) compensation = projectileVelocity / distance;
|
||||
|
|
|
|||
|
|
@ -59,25 +59,27 @@ void drawOptionsMenu(int deltaTime, int currentOptions[], bool click) {
|
|||
);
|
||||
|
||||
filledCircleColor(screen, (float) screen -> w / 100 * (30 + 0.4 * 100 * (float)(currentOptions[i] - minOptions[i]) / (maxOptions[i] - minOptions[i])),
|
||||
(float) screen -> h / 100 * (optionsStart + i * optionsTav), larger(4, 20 * screen -> h / designHeight), 0xFFFFFFFF
|
||||
(float) screen -> h / 100 * (optionsStart + i * optionsTav), larger(4, (int)(20 * uiScale())), 0xFFFFFFFF
|
||||
);
|
||||
aacircleColor(screen, (float) screen -> w / 100 * (30 + 0.4 * 100 * (float)(currentOptions[i] - minOptions[i]) / (maxOptions[i] - minOptions[i])),
|
||||
(float) screen -> h / 100 * (optionsStart + i * optionsTav), larger(4, 20 * screen -> h / designHeight), 0xFFFFFFFF
|
||||
(float) screen -> h / 100 * (optionsStart + i * optionsTav), larger(4, (int)(20 * uiScale())), 0xFFFFFFFF
|
||||
);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(click)
|
||||
for(i = 0; i < 5; i++)
|
||||
if(click) {
|
||||
for(i = 0; i < 5; i++) {
|
||||
if(isMouseOverRect(50, optionsStart + optionsTav * i, 50, optionsTav / 2)) {
|
||||
//Ezek nem magic számok, csak egy kicsit.
|
||||
currentOptions[i] = minOptions[i] + (int)((maxOptions[i] - minOptions[i]) * (getMousePosition().x - screen -> w * 0.3) / (screen -> w * 0.4) );
|
||||
putInsideInterval(minOptions[i], ¤tOptions[i], maxOptions[i]);
|
||||
//Ezek nem magic számok, csak egy kicsit.
|
||||
currentOptions[i] = minOptions[i] + (int)((maxOptions[i] - minOptions[i]) * (getMousePosition().x - screen -> w * 0.3) / (screen -> w * 0.4) );
|
||||
putInsideInterval(minOptions[i], ¤tOptions[i], maxOptions[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printBack();
|
||||
drawMenuParticles(deltaTime);
|
||||
printBack();
|
||||
drawMenuParticles(deltaTime);
|
||||
}
|
||||
|
||||
///A játék leírását rajzolja meg.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "menuFSM.h"
|
||||
#include "../touch.h"
|
||||
|
||||
|
||||
int currentOptions[] = {2000, 2000, 3, 450, 6000};
|
||||
|
|
@ -86,6 +87,14 @@ void handleClicks(bool* prematureClick) {
|
|||
if(isMouseOverRect(50, menuStart + menuTav*5, 15, menuTav/2))
|
||||
gameState = mainMenu;
|
||||
break;
|
||||
|
||||
case nameInput:
|
||||
case fading:
|
||||
case initGame:
|
||||
case initLoadedGame:
|
||||
case quit:
|
||||
//ezekben az állapotokban a kattintásnak nincs hatása
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,8 +192,16 @@ void doStuff(bool isLMBDown, bool isRMBDown, bool prematureClick) {
|
|||
snprintf(loseText, sizeof(loseText), "%s%s vesztett, a pontszáma %d.", typedName, cursorBlinking > 500 ? "|" : "", getPoints());
|
||||
utf8PrintCentered(loseText, 50, 50, small);
|
||||
break;
|
||||
|
||||
case quit:
|
||||
//a kilépésről a main gondoskodik, itt már nincs mit rajzolni
|
||||
break;
|
||||
}
|
||||
//érintéssel nincs kurzor, helyette a képernyőre rajzolt gombok látszanak
|
||||
if(isAlive()) {
|
||||
drawTouchControls();
|
||||
if(shouldDrawCursor()) drawMouse(isInSlowMo);
|
||||
}
|
||||
if(isAlive()) drawMouse(isInSlowMo);
|
||||
}
|
||||
|
||||
///A nameInputból returnnel és ESC-vel is ki lehet lépni, ezért kell ez a függvény rá, amit mindkét esetben meg lehet hívni.
|
||||
|
|
@ -238,6 +255,30 @@ void processTyping(SDL_Event event) {
|
|||
}
|
||||
}
|
||||
|
||||
/** \brief A név teljes tartalmát cseréli le, a böngésző beviteli mezőjéhez.
|
||||
* A mobilos billentyűzet nem karakterenként érkezik: a html mező mindig a teljes szövegét
|
||||
* küldi el, mert így a törlés, a javítás és a szövegbeillesztés is magától jó lesz.
|
||||
*/
|
||||
void setNameInput(const char* utf8) {
|
||||
if(gameState != nameInput) return;
|
||||
|
||||
wchar_t typed[sizeof(name) / sizeof(wchar_t)];
|
||||
int count = utf8ToWide(utf8, typed, sizeof(typed) / sizeof(wchar_t));
|
||||
|
||||
nameIndex = 0;
|
||||
int i;
|
||||
for(i = 0; i < count; i++)
|
||||
if(isValidChar(typed[i]) && nameIndex < (int) (sizeof(name) / sizeof(wchar_t)) - 1)
|
||||
name[nameIndex++] = typed[i];
|
||||
|
||||
name[nameIndex] = 0;
|
||||
}
|
||||
|
||||
///A böngésző beviteli mezőjének lezárása ugyanaz, mint az enter megnyomása.
|
||||
void submitNameInput() {
|
||||
escapeFromNameInput();
|
||||
}
|
||||
|
||||
///A beírt karaktereket utf-8-ból átalakítva fűzi a névhez.
|
||||
void processTextInput(const char* utf8) {
|
||||
if(gameState == nameInput) {
|
||||
|
|
|
|||
|
|
@ -18,5 +18,7 @@
|
|||
void whatToDoOnEscape();
|
||||
void processTyping(SDL_Event event);
|
||||
void processTextInput(const char* utf8);
|
||||
void setNameInput(const char* utf8);
|
||||
void submitNameInput();
|
||||
|
||||
#endif // MENUFSM_H_INCLUDED
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
#define particleSizeVariance 0.05 /**< Az eső részecskék (~hó) méretének átlagostól való eltérése [m]. */
|
||||
#define particleBaseVelocity 0.0025 /**< Az eső részecskék (~hó) átlagos sebessége [m/ms]. */
|
||||
#define particleVelocityVariance 0.0010 /**< Az eső részecskék (~hó) sebességének átlagostól való eltérése [m/ms]. */
|
||||
#define particleMinDistance 5 /**< Ennél közelebb a kamerához egy részecske sem kerülhet [m]. */
|
||||
#define particleMinZ 1.0 /**< A pályához legközelebbi részecskesík, a kockák előlapja z = 1-nél van [m]. */
|
||||
#define particleMaxZ (cameraDeltaZDistanceEnd - particleMinDistance) /**< A kamerához legközelebbi részecskesík [m]. */
|
||||
#define particleColor (colors) { (color) {220, 220, 220, 255}, (color){220, 220, 220, 255}, (color) {220, 220, 220, 255} } /**< A részecskék színe. */
|
||||
|
||||
int particleMultiplier; /**< Egy oszlopban hány részecske van? */
|
||||
|
|
@ -15,10 +18,16 @@ void setParticleMultiplier(int a, bool isMenu) {
|
|||
particleMultiplier = isMenu ? a : getMapSize().y * a / 100;
|
||||
}
|
||||
|
||||
///A makrókban beállított paraméterek alapján hoz létre egy részecskét, a pálya és a kamera közti z koorddal.
|
||||
/** \brief A makrókban beállított paraméterek alapján hoz létre egy részecskét, a pálya és a kamera közti z koorddal.
|
||||
* A z koordot nem a teljes [pálya, kamera] szakaszról sorsoljuk: a kamera modellje szerint egy test látszólagos
|
||||
* mérete a lencsétől mért távolságával fordítottan arányos, így a lencse elé kerülő részecske nem hópehelynek,
|
||||
* hanem a képernyőn átsuhanó fehér csíknak látszana. A kamera legközelebbi állása (a dolly vége) a
|
||||
* cameraDeltaZDistanceEnd, ezért ehhez képest hagyunk particleMinDistance ráhagyást.
|
||||
*/
|
||||
particle createRandomParticle(int baseY, bool isYRand) {
|
||||
return (particle) {
|
||||
(vector2D) {isYRand ? rand() % (baseY-1) : baseY, (float) (rand() % (cameraDeltaZDistanceEnd - 1) + 1)},
|
||||
(vector2D) {isYRand ? rand() % (baseY-1) : baseY,
|
||||
randomWithinBorders((particleMaxZ + particleMinZ) / 2, (particleMaxZ - particleMinZ) / 2)},
|
||||
randomWithinBorders(particleBaseSize, particleSizeVariance),
|
||||
randomWithinBorders(particleBaseVelocity, particleVelocityVariance)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,13 +63,11 @@ void movePlayer(unsigned char* keys, int deltaTime) {
|
|||
//<\input feldolgozása>
|
||||
//<sebesség kiszámítása>
|
||||
if(directionOfMovement != 0) {
|
||||
//billentyű lenyomás hatására elkezd gyorsulni
|
||||
if(abs(playerVelocity.x) < playerMaxVelocity.x) {
|
||||
//hogy ne lehesen gyorsabb a max sebességénél
|
||||
if(playerVelocity.x > playerMaxVelocity.x) playerVelocity.x = playerMaxVelocity.x;
|
||||
else if(playerVelocity.x < -playerMaxVelocity.x) playerVelocity.x = - playerMaxVelocity.x;
|
||||
playerVelocity.x += directionOfMovement * playerXAcceleration * deltaTime;
|
||||
}
|
||||
//billentyű lenyomás hatására elkezd gyorsulni, irányváltáskor pedig lefékez, majd megfordul
|
||||
playerVelocity.x += directionOfMovement * playerXAcceleration * deltaTime;
|
||||
//hogy ne lehesen gyorsabb a max sebességénél
|
||||
if(playerVelocity.x > playerMaxVelocity.x) playerVelocity.x = playerMaxVelocity.x;
|
||||
else if(playerVelocity.x < -playerMaxVelocity.x) playerVelocity.x = - playerMaxVelocity.x;
|
||||
}
|
||||
else {
|
||||
//lassulva megáll
|
||||
|
|
|
|||
375
Forráskód/modules/touch.c
Normal file
375
Forráskód/modules/touch.c
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
#include "touch.h"
|
||||
#include "player.h"
|
||||
#include "menu/menuFSM.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
/** \file touch.c
|
||||
* Érintőképernyős vezérlés. A játék eredetileg billentyűzetre és egérre készült, itt
|
||||
* fordítjuk le az ujjak mozgását ugyanerre: a mozgatógombok a billentyűzet állapotát
|
||||
* írják át, a szabad felületre koppintás pedig egérkattintásként viselkedik. Így a
|
||||
* játék többi része semmit nem tud arról, hogy épp érintéssel játszanak-e.
|
||||
*/
|
||||
|
||||
#define maxFingers 10 /**< Ennél több ujjat úgyse tesz senki a képernyőre. */
|
||||
#define noButton (-1) /**< Ez az ujj nem gombon van, tehát egérként viselkedik. */
|
||||
|
||||
typedef enum touchButton {
|
||||
touchLeft, touchRight, touchJump, touchSlow, touchPause, touchButtonCount
|
||||
} touchButton;
|
||||
|
||||
typedef struct touchArea {
|
||||
int x, y, r;
|
||||
} touchArea;
|
||||
|
||||
typedef struct fingerState {
|
||||
SDL_FingerID id;
|
||||
bool isActive;
|
||||
int button;
|
||||
} fingerState;
|
||||
|
||||
static bool touchMode = false;
|
||||
static bool isLastInputTouch = false; /**< Érintőképernyős laptopon az egér és az ujj is jó, mindig az utóbb használt számít. */
|
||||
static touchArea areas[touchButtonCount];
|
||||
static bool isHeld[touchButtonCount];
|
||||
static bool isForced[touchButtonCount];
|
||||
static fingerState fingers[maxFingers];
|
||||
|
||||
static const SDL_Scancode buttonKeys[touchButtonCount] = {
|
||||
SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_SPACE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
|
||||
};
|
||||
|
||||
///Érintőképernyős eszközön futunk-e? Az első ujjlenyomás ezt amúgy is bekapcsolja.
|
||||
void initTouch() {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
touchMode = EM_ASM_INT({
|
||||
return (navigator.maxTouchPoints > 0 || 'ontouchstart' in window) ? 1 : 0;
|
||||
});
|
||||
//érintésre képes eszközön alapból ne villanjon fel a bal felső sarokban a kurzor
|
||||
isLastInputTouch = touchMode;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isTouchMode() {
|
||||
return touchMode;
|
||||
}
|
||||
|
||||
///Az egérrel célzáshoz kell a rajzolt kurzor, ujjal viszont csak útban lenne.
|
||||
bool shouldDrawCursor() {
|
||||
return !isLastInputTouch;
|
||||
}
|
||||
|
||||
///Egérhasználatkor visszakapcsol az egeres megjelenésre.
|
||||
void noteMouseInput() {
|
||||
isLastInputTouch = false;
|
||||
}
|
||||
|
||||
///Csak a játék közben, életben lévő játékosnál van értelme a gomboknak.
|
||||
static bool areControlsVisible() {
|
||||
return touchMode && isLastInputTouch && screen != NULL && getGameState() == game && isAlive();
|
||||
}
|
||||
|
||||
/** \brief Kiszámolja a gombok helyét a képernyő aktuális méretéhez.
|
||||
* A gombok mérete a kisebbik képernyőoldalhoz igazodik, így fekvő és álló tájolásban is
|
||||
* hüvelykujjal elérhető marad. Minden képkockában újraszámoljuk, mert az ablak bármikor
|
||||
* változhat, és így nem kell külön értesítést kötni az átméretezéshez.
|
||||
*/
|
||||
static void layoutButtons() {
|
||||
if(screen == NULL) return;
|
||||
|
||||
int unit = smaller(screen -> w, screen -> h);
|
||||
int r = larger(28, (int)(unit * 0.1));
|
||||
int margin = (int)(unit * 0.055) + r;
|
||||
int gap = (int)(r * 0.55);
|
||||
|
||||
areas[touchLeft] = (touchArea) {margin, screen -> h - margin, r};
|
||||
areas[touchRight] = (touchArea) {margin + 2 * r + gap, screen -> h - margin, r};
|
||||
areas[touchJump] = (touchArea) {screen -> w - margin, screen -> h - margin, r};
|
||||
areas[touchSlow] = (touchArea) {screen -> w - margin, screen -> h - margin - 2 * r - gap, r};
|
||||
|
||||
//a szünet gomb szorosan a sarokba kerül, különben álló tájolásban a pontszámra lógna
|
||||
int pauseRadius = (int)(r * 0.6);
|
||||
int pauseInset = (int)(unit * 0.035) + pauseRadius;
|
||||
areas[touchPause] = (touchArea) {screen -> w - pauseInset, pauseInset, pauseRadius};
|
||||
}
|
||||
|
||||
///Melyik gombra esik a megadott pont? Kicsit nagyobb sugárral számolunk, mert az ujj pontatlan.
|
||||
static int buttonAt(int x, int y) {
|
||||
int i;
|
||||
for(i = 0; i < touchButtonCount; i++) {
|
||||
float reach = areas[i].r * 1.2f;
|
||||
if(square(x - areas[i].x) + square(y - areas[i].y) < square(reach)) return i;
|
||||
}
|
||||
return noButton;
|
||||
}
|
||||
|
||||
static fingerState* findFinger(SDL_FingerID id) {
|
||||
int i;
|
||||
for(i = 0; i < maxFingers; i++)
|
||||
if(fingers[i].isActive && fingers[i].id == id) return &fingers[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static fingerState* addFinger(SDL_FingerID id) {
|
||||
fingerState* existing = findFinger(id);
|
||||
if(existing != NULL) return existing;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < maxFingers; i++)
|
||||
if(!fingers[i].isActive) {
|
||||
fingers[i] = (fingerState) {id, true, noButton};
|
||||
return &fingers[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///Az ujjak alapján újraszámolja, melyik gomb van épp lenyomva.
|
||||
static void refreshHeldButtons() {
|
||||
int i;
|
||||
for(i = 0; i < touchButtonCount; i++) isHeld[i] = false;
|
||||
|
||||
for(i = 0; i < maxFingers; i++)
|
||||
if(fingers[i].isActive && fingers[i].button != noButton)
|
||||
isHeld[fingers[i].button] = true;
|
||||
}
|
||||
|
||||
/** \brief Az ujjakhoz tartozó eventeket kezeli.
|
||||
* A gombokra eső ujjak a gombjukhoz tapadnak, a többi ujj egérként viselkedik: mozgatja a
|
||||
* kurzort és kattint is vele. Emiatt lehet egyszerre menni, ugrani és lőni, amit egyetlen
|
||||
* emulált egérrel nem lehetne.
|
||||
* \param a feldolgozandó event
|
||||
* \param a bal egérgomb állapota, ezt a hívó adja át a játéknak
|
||||
* \param prematureClick a beállítás menü miatt kell
|
||||
*/
|
||||
void handleFingerEvent(SDL_Event* event, bool* isLMBDown, bool* prematureClick) {
|
||||
if(screen == NULL) return;
|
||||
|
||||
touchMode = isLastInputTouch = true;
|
||||
layoutButtons();
|
||||
|
||||
int x = (int)(event -> tfinger.x * screen -> w);
|
||||
int y = (int)(event -> tfinger.y * screen -> h);
|
||||
putInsideInterval(0, &x, screen -> w - 1);
|
||||
putInsideInterval(0, &y, screen -> h - 1);
|
||||
|
||||
fingerState* finger;
|
||||
|
||||
switch(event -> type) {
|
||||
case SDL_FINGERDOWN:
|
||||
finger = addFinger(event -> tfinger.fingerId);
|
||||
if(finger == NULL) break;
|
||||
|
||||
finger -> button = areControlsVisible() ? buttonAt(x, y) : noButton;
|
||||
|
||||
if(finger -> button == noButton) {
|
||||
setMousePosition(x, y);
|
||||
*isLMBDown = true;
|
||||
handleClicks(prematureClick);
|
||||
}
|
||||
//a szünet gomb kattintásként viselkedik, nem tartásként
|
||||
else if(finger -> button == touchPause) whatToDoOnEscape();
|
||||
break;
|
||||
|
||||
case SDL_FINGERMOTION:
|
||||
finger = findFinger(event -> tfinger.fingerId);
|
||||
if(finger == NULL) break;
|
||||
|
||||
if(finger -> button == noButton) setMousePosition(x, y);
|
||||
//a két iránygomb közt át lehet csúsztatni az ujjat, a többi gomb megtartja az ujját
|
||||
else if(finger -> button == touchLeft || finger -> button == touchRight) {
|
||||
int moved = buttonAt(x, y);
|
||||
if(moved == touchLeft || moved == touchRight) finger -> button = moved;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_FINGERUP:
|
||||
finger = findFinger(event -> tfinger.fingerId);
|
||||
if(finger == NULL) break;
|
||||
|
||||
if(finger -> button == noButton) *isLMBDown = *prematureClick = false;
|
||||
finger -> isActive = false;
|
||||
break;
|
||||
}
|
||||
|
||||
refreshHeldButtons();
|
||||
}
|
||||
|
||||
bool isTouchSlowMo() {
|
||||
return touchMode && isHeld[touchSlow];
|
||||
}
|
||||
|
||||
/** \brief A megnyomott iránygombokat billentyűlenyomásnak állítja be.
|
||||
* Csak azokat a billentyűket írja át, amiket ő maga nyomott le, így egy érintőképernyős
|
||||
* laptopon a valódi billentyűzet is használható marad.
|
||||
*/
|
||||
void applyTouchKeys() {
|
||||
if(!touchMode || keys == NULL) return;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < touchButtonCount; i++) {
|
||||
if(buttonKeys[i] == SDL_SCANCODE_UNKNOWN) continue;
|
||||
|
||||
if(isHeld[i]) {
|
||||
keys[buttonKeys[i]] = 1;
|
||||
isForced[i] = true;
|
||||
}
|
||||
else if(isForced[i]) {
|
||||
keys[buttonKeys[i]] = 0;
|
||||
isForced[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Egy gomb korongja, lenyomva világosabb.
|
||||
static void drawButtonBase(touchArea area, bool pressed) {
|
||||
filledCircleColor(screen, area.x, area.y, area.r, pressed ? 0xFFFFFF44 : 0xFFFFFF1E);
|
||||
aacircleColor(screen, area.x, area.y, area.r, pressed ? 0xFFFFFFCC : 0xFFFFFF66);
|
||||
}
|
||||
|
||||
///Háromszög alakú nyíl, az irány -1 (balra), +1 (jobbra) vagy 0 (felfelé).
|
||||
static void drawArrow(touchArea area, int direction) {
|
||||
float size = area.r * 0.45f;
|
||||
float vx[3], vy[3];
|
||||
|
||||
if(direction == 0) {
|
||||
vx[0] = area.x; vy[0] = area.y - size;
|
||||
vx[1] = area.x - size; vy[1] = area.y + size * 0.8;
|
||||
vx[2] = area.x + size; vy[2] = area.y + size * 0.8;
|
||||
}
|
||||
else {
|
||||
vx[0] = area.x + direction * size; vy[0] = area.y;
|
||||
vx[1] = area.x - direction * size * 0.8; vy[1] = area.y - size;
|
||||
vx[2] = area.x - direction * size * 0.8; vy[2] = area.y + size;
|
||||
}
|
||||
|
||||
filledPolygonRGBA(screen, vx, vy, 3, 255, 255, 255, 220);
|
||||
aapolygonRGBA(screen, vx, vy, 3, 255, 255, 255, 220);
|
||||
}
|
||||
|
||||
///Az időlassítás ikonja egy óra, a szünet gombé két álló csík.
|
||||
static void drawSlowMoIcon(touchArea area) {
|
||||
Sint16 size = (Sint16)(area.r * 0.45);
|
||||
aacircleColor(screen, area.x, area.y, size, 0xFFFFFFDC);
|
||||
lineColor(screen, area.x, area.y, area.x, area.y - (Sint16)(size * 0.7), 0xFFFFFFDC);
|
||||
lineColor(screen, area.x, area.y, area.x + (Sint16)(size * 0.5), area.y, 0xFFFFFFDC);
|
||||
}
|
||||
|
||||
static void drawPauseIcon(touchArea area) {
|
||||
int width = larger(2, (int)(area.r * 0.16));
|
||||
int height = (int)(area.r * 0.45);
|
||||
int gap = (int)(area.r * 0.2);
|
||||
|
||||
boxRGBA(screen, area.x - gap - width, area.y - height, area.x - gap, area.y + height, 255, 255, 255, 220);
|
||||
boxRGBA(screen, area.x + gap, area.y - height, area.x + gap + width, area.y + height, 255, 255, 255, 220);
|
||||
}
|
||||
|
||||
///Kirajzolja a képernyőre az érintéses gombokat, egérrel játszva semmit nem csinál.
|
||||
void drawTouchControls() {
|
||||
if(!areControlsVisible()) return;
|
||||
|
||||
layoutButtons();
|
||||
|
||||
int i;
|
||||
for(i = 0; i < touchButtonCount; i++) drawButtonBase(areas[i], isHeld[i]);
|
||||
|
||||
drawArrow(areas[touchLeft], -1);
|
||||
drawArrow(areas[touchRight], 1);
|
||||
drawArrow(areas[touchJump], 0);
|
||||
drawSlowMoIcon(areas[touchSlow]);
|
||||
drawPauseIcon(areas[touchPause]);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
|
||||
/** \brief Megjeleníti a névbeíráshoz tartozó html mezőt.
|
||||
* Az emscripten SDL2-je nem hozza elő a mobilos billentyűzetet, ráadásul iOS-en csak a
|
||||
* felhasználó koppintására lehet fókuszt adni egy mezőnek. Ezért egy látható beviteli
|
||||
* mezőt teszünk a vászon fölé, amit a játékos maga koppint meg.
|
||||
*/
|
||||
EM_JS(void, showNameOverlay, (), {
|
||||
var box = document.getElementById('nameOverlay');
|
||||
|
||||
if (!box) {
|
||||
box = document.createElement('form');
|
||||
box.id = 'nameOverlay';
|
||||
box.style.cssText =
|
||||
'position:fixed;left:50%;top:62%;transform:translate(-50%,-50%);z-index:10;' +
|
||||
'display:flex;gap:8px;padding:10px;border-radius:12px;' +
|
||||
'background:rgba(26,5,5,0.92);border:1px solid rgba(232,213,213,0.35);' +
|
||||
'font:16px system-ui,sans-serif;touch-action:manipulation;';
|
||||
|
||||
var field = document.createElement('input');
|
||||
field.type = 'text';
|
||||
field.maxLength = 40;
|
||||
field.placeholder = 'Neved';
|
||||
field.autocomplete = 'off';
|
||||
field.autocapitalize = 'off';
|
||||
field.style.cssText =
|
||||
'width:9em;min-width:0;padding:10px 12px;font:inherit;color:#e8d5d5;' +
|
||||
'background:rgba(255,255,255,0.08);border:0;border-radius:8px;outline:none;';
|
||||
|
||||
var done = document.createElement('button');
|
||||
done.type = 'submit';
|
||||
done.textContent = 'Kész';
|
||||
done.style.cssText =
|
||||
'padding:10px 16px;font:inherit;color:#1a0505;background:#e8d5d5;' +
|
||||
'border:0;border-radius:8px;';
|
||||
|
||||
box.appendChild(field);
|
||||
box.appendChild(done);
|
||||
document.body.appendChild(box);
|
||||
|
||||
field.addEventListener('input', function () {
|
||||
Module.ccall('mobileNameChanged', null, ['string'], [field.value]);
|
||||
});
|
||||
|
||||
box.addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
field.blur();
|
||||
Module.ccall('mobileNameSubmitted', null, [], []);
|
||||
});
|
||||
}
|
||||
|
||||
var current = box.querySelector('input');
|
||||
current.value = "";
|
||||
box.style.display = 'flex';
|
||||
});
|
||||
|
||||
EM_JS(void, hideNameOverlay, (), {
|
||||
var box = document.getElementById('nameOverlay');
|
||||
if (box) {
|
||||
box.querySelector('input').blur();
|
||||
box.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
///A html mezőbe írt teljes nevet veszi át.
|
||||
EMSCRIPTEN_KEEPALIVE void mobileNameChanged(const char* utf8) {
|
||||
setNameInput(utf8);
|
||||
}
|
||||
|
||||
///A "Kész" gomb megnyomását továbbítja.
|
||||
EMSCRIPTEN_KEEPALIVE void mobileNameSubmitted() {
|
||||
submitNameInput();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///A névbeíró állapotba lépve előveszi, kilépve elrejti a böngésző beviteli mezőjét.
|
||||
void syncSoftKeyboard(states state) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
static bool isShown = false;
|
||||
bool isWanted = touchMode && state == nameInput;
|
||||
|
||||
if(isWanted == isShown) return;
|
||||
isShown = isWanted;
|
||||
|
||||
if(isWanted) showNameOverlay();
|
||||
else hideNameOverlay();
|
||||
#else
|
||||
(void) state;
|
||||
#endif
|
||||
}
|
||||
17
Forráskód/modules/touch.h
Normal file
17
Forráskód/modules/touch.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef TOUCH_H_INCLUDED
|
||||
#define TOUCH_H_INCLUDED
|
||||
|
||||
#include "global.h"
|
||||
#include "menu/menuDraw.h"
|
||||
|
||||
void initTouch();
|
||||
bool isTouchMode();
|
||||
bool shouldDrawCursor();
|
||||
void noteMouseInput();
|
||||
void handleFingerEvent(SDL_Event* event, bool* isLMBDown, bool* prematureClick);
|
||||
bool isTouchSlowMo();
|
||||
void applyTouchKeys();
|
||||
void drawTouchControls();
|
||||
void syncSoftKeyboard(states state);
|
||||
|
||||
#endif // TOUCH_H_INCLUDED
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="hu">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
|
||||
<title>Platform játék</title>
|
||||
<style>
|
||||
html, body {
|
||||
|
|
@ -12,6 +12,15 @@
|
|||
color: #e8d5d5;
|
||||
font: 16px/1.5 system-ui, sans-serif;
|
||||
overflow: hidden;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
#shell, canvas {
|
||||
touch-action: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
#shell {
|
||||
|
|
@ -27,33 +36,23 @@
|
|||
background: #280000;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#status {
|
||||
position: fixed;
|
||||
bottom: 12px;
|
||||
font-size: 14px;
|
||||
opacity: 0.65;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="shell">
|
||||
<canvas id="canvas" tabindex="-1" oncontextmenu="event.preventDefault()"></canvas>
|
||||
</div>
|
||||
<div id="status">Betöltés…</div>
|
||||
<script>
|
||||
var statusElement = document.getElementById('status');
|
||||
var canvasElement = document.getElementById('canvas');
|
||||
|
||||
var Module = {
|
||||
canvas: canvasElement,
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
postRun: [function () {
|
||||
canvasElement.focus();
|
||||
}],
|
||||
print: function (text) { console.log(text); },
|
||||
printErr: function (text) { console.error(text); },
|
||||
setStatus: function (text) {
|
||||
if (text) statusElement.textContent = text;
|
||||
}
|
||||
printErr: function (text) { console.error(text); }
|
||||
};
|
||||
|
||||
canvasElement.addEventListener('click', function () { canvasElement.focus(); });
|
||||
|
|
|
|||
Loading…
Reference in a new issue