From c0f7f5fbd8a0e87cfeb9823e97c67c65ac854376 Mon Sep 17 00:00:00 2001 From: Horoli Date: Mon, 8 Jun 2026 15:09:27 +0900 Subject: [PATCH] Reduce mobile canvas memory usage --- src/constants.js | 31 +++++++++++++++++++------------ src/game/combat/specialEffects.js | 25 +++++++++++++++++++++++-- src/main.js | 11 ++++++++--- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/constants.js b/src/constants.js index 599414a..137c2e5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,6 +2,8 @@ const GRID_SIZE = 50; const TILE_SIZE = 64; const ARENA_SIZE = GRID_SIZE * TILE_SIZE; +const VIEWPORT_SIZE = 1600; +const CAMERA_ZOOM_SCALE = VIEWPORT_SIZE / ARENA_SIZE; export const ARENA = { GRID_SIZE, @@ -9,6 +11,11 @@ export const ARENA = { SIZE: ARENA_SIZE, }; +export const RENDER = { + VIEWPORT_SIZE, + CAMERA_ZOOM_SCALE, +}; + // 2. FIGHTER 도메인 export const FIGHTER = { SCALE: 3, @@ -235,13 +242,13 @@ export const SPECIAL_EFFECT = { alpha: 1, }, CAMERA: { - ZOOM: 3, + ZOOM: 3 * CAMERA_ZOOM_SCALE, CENTER_ON_CASTER_AT_START: true, ZOOM_IN_MS: 720, HOLD_MS: 1100, ZOOM_OUT_MS: 1300, LERP: 0.045, - PROJECTILE_VIEW_ZOOM: 1, + PROJECTILE_VIEW_ZOOM: 1 * CAMERA_ZOOM_SCALE, PROJECTILE_ZOOM_OUT_MS: 300, }, FOCUS_LAYER: { @@ -330,31 +337,31 @@ export const WORLD_EFFECT = { // 7. CAMERA 도메인 export const CAMERA = { - MIN_ZOOM: 1, - MAX_ZOOM: 3, - ZOOM_STEP: 0.1, + MIN_ZOOM: 1 * CAMERA_ZOOM_SCALE, + MAX_ZOOM: 3 * CAMERA_ZOOM_SCALE, + ZOOM_STEP: 0.1 * CAMERA_ZOOM_SCALE, // 자동 관전 진입 전 화염/냉기 메테오 낙하 위치를 임시로 확대 추적합니다. METEOR_FOCUS_ENABLED: false, - METEOR_FOCUS_ZOOM: 2, + METEOR_FOCUS_ZOOM: 2 * CAMERA_ZOOM_SCALE, SPECTATOR_LERP: 0.01, // 메테오 착탄 후 카메라를 해당 위치에 유지하는 시간(ms)입니다. METEOR_FOCUS_HOLD_DURATION: 1200, SPECTATOR_FINAL_FIGHTER_THRESHOLD: 5, - SPECTATOR_FINAL_FIGHT_ZOOM: 3, + SPECTATOR_FINAL_FIGHT_ZOOM: 3 * CAMERA_ZOOM_SCALE, SPECTATOR_FINAL_TEAM_COUNT: 2, SPECTATOR_FINAL_TEAM_TOTAL_THRESHOLD: 8, SPECTATOR_RANDOM_FOCUS_INTERVAL: 10000, SPECTATOR_LATE_FIGHTER_THRESHOLD: 500, - SPECTATOR_LATE_FIGHT_ZOOM: 2, - SELECTED_FIGHTER_ZOOM: 2, + SPECTATOR_LATE_FIGHT_ZOOM: 2 * CAMERA_ZOOM_SCALE, + SELECTED_FIGHTER_ZOOM: 2 * CAMERA_ZOOM_SCALE, }; // 8. UI 도메인 export const UI = { MINIMAP_ALPHA: 0.8, - MINIMAP_MARGIN: Math.round(ARENA_SIZE * 0.016), - MINIMAP_VIEWPORT_SIZE: Math.round(ARENA_SIZE * 0.22), - MINIMAP_VIEW_FRAME_STROKE: 10, + MINIMAP_MARGIN: Math.round(VIEWPORT_SIZE * 0.016), + MINIMAP_VIEWPORT_SIZE: Math.round(VIEWPORT_SIZE * 0.22), + MINIMAP_VIEW_FRAME_STROKE: Math.max(4, Math.round(VIEWPORT_SIZE * 0.003125)), SELECTED_FIGHTER_OUTLINE_GAP: 1, SELECTED_FIGHTER_OUTLINE_WIDTH: 1, SELECTED_FIGHTER_OUTLINE_RED: 255, diff --git a/src/game/combat/specialEffects.js b/src/game/combat/specialEffects.js index 6641403..ad3c857 100644 --- a/src/game/combat/specialEffects.js +++ b/src/game/combat/specialEffects.js @@ -1,5 +1,5 @@ import Phaser from "phaser"; -import { ARENA, FIGHTER, SPECIAL_EFFECT } from "../../constants.js"; +import { ARENA, FIGHTER, RENDER, SPECIAL_EFFECT } from "../../constants.js"; import { applySpecialEffectInstantKill, disposeCombatObject, @@ -1032,6 +1032,13 @@ function shouldDrawInSpecialFocusSnapshot( } function canCreateArenaRenderTexture(scene) { + if ( + isMobileRuntime() + && ARENA.SIZE * ARENA.SIZE > RENDER.VIEWPORT_SIZE * RENDER.VIEWPORT_SIZE + ) { + return false; + } + const renderer = scene.game?.renderer; const gl = renderer?.gl; @@ -1039,7 +1046,21 @@ function canCreateArenaRenderTexture(scene) { return true; } - return gl.getParameter(gl.MAX_TEXTURE_SIZE) >= ARENA.SIZE; + return Math.min( + gl.getParameter(gl.MAX_TEXTURE_SIZE), + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE), + ) >= ARENA.SIZE; +} + +function isMobileRuntime() { + const navigator = globalThis.navigator; + const userAgent = navigator?.userAgent ?? ""; + + return Boolean( + navigator?.userAgentData?.mobile + || /Android|iPhone|iPad|iPod/i.test(userAgent) + || globalThis.matchMedia?.("(pointer: coarse) and (max-width: 960px)")?.matches, + ); } function destroySpecialFocusLayer(scene, state) { diff --git a/src/main.js b/src/main.js index df2fc8c..a2cedc7 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,7 @@ import Phaser from "phaser"; import { ArenaScene } from "./game/arena/ArenaScene.js"; import { - ARENA, + RENDER, SPAWN, } from "./constants.js"; import { createMatchForm } from "./ui/matchForm.js"; @@ -181,10 +181,15 @@ const arenaScene = new ArenaScene({ const game = new Phaser.Game({ type: Phaser.AUTO, parent: "game", - width: ARENA.SIZE, - height: ARENA.SIZE, + width: RENDER.VIEWPORT_SIZE, + height: RENDER.VIEWPORT_SIZE, pixelArt: true, backgroundColor: "#282819", + render: { + antialias: false, + pixelArt: true, + roundPixels: true, + }, physics: { default: "arcade", arcade: {