Reduce mobile canvas memory usage

This commit is contained in:
Horoli 2026-06-08 15:09:27 +09:00
parent da8ae49a72
commit c0f7f5fbd8
3 changed files with 50 additions and 17 deletions

View File

@ -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,

View File

@ -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) {

View File

@ -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: {