Files
erqiwang_youle/client/js/gameabc-framework/templates/LayoutConstants.template.js
T

501 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ============================================================================
* LayoutConstants.template.js - 游戏布局常量规范模板
* ============================================================================
*
* 使用方式:
* 将此文件复制到游戏目录,重命名为 LayoutConstants.js,
* 然后按游戏实际分辨率和精灵编排修改各项数值。
*
* 如布局配置过多,可拆分为多个文件(如 PopupLayoutConstants.js),
* 每个文件都向同一个全局对象 LayoutConstants 挂载属性。
*
* 加载顺序(index.html 中只需保证在 CardLayoutManager.js 之前加载即可):
* <script src="js/01_SubGame/codes/game/constants/sprites/LayoutConstants.js"></script>
* <script src="js/01_SubGame/codes/game/constants/sprites/PopupLayoutConstants.js"></script>
* <script src="js/01_SubGame/codes/game/managers/CardLayoutManager.js"></script>
*
* 文件不依赖框架,可单独加载。
*
* ============================================================================
* @guide 核心设计理念
* ============================================================================
*
* LayoutConstants 是一个 **纯数据文件**(无函数、无副作用),
* 所有坐标/尺寸/偏移量都在这里集中定义,供各 Manager / View 读取。
*
* 原则:
* 1. 只包含常量对象,不包含业务逻辑
* 2. 每个布局区域单独一个顶层属性(HAND_CARD_LAYOUT, DISCARD_LAYOUT …)
* 3. 4人麻将用 PLAYER_1..4 子对象;其他游戏可用 SELF/OPPONENT 等命名
* 4. 透视偏移量(PERSPECTIVE_OFFSET_X/Y)用于 2D 模拟 3D 效果,正对视角均为 0
* 5. DIRECTION 枚举:'horizontal' | 'horizontal-reverse' | 'vertical' | 'vertical-reverse'
* 6. ALIGN_MODE 枚举:'start' | 'center' | 'end'
*
* ============================================================================
* @guide 透视偏移量说明
* ============================================================================
*
* gameabc 以 2D 图片模拟 3D 透视效果。每张牌在排列时叠加一个累积偏移:
*
* 最终X = 基准X + 牌索引 × PERSPECTIVE_OFFSET_X
* 最终Y = 基准Y + 牌索引 × PERSPECTIVE_OFFSET_Y
*
* 正值向右/向下,负值向左/向上。
* 玩家1(正对屏幕)通常 PERSPECTIVE_OFFSET = 0。
* 玩家2/4(侧视角)需要配置非零值形成倾斜效果。
*
* ============================================================================
* @guide 如何拆分为多文件
* ============================================================================
*
* // LayoutConstants.js(主游戏界面)
* var LayoutConstants = {};
* LayoutConstants.HAND_CARD_LAYOUT = { ... };
* LayoutConstants.DISCARD_LAYOUT = { ... };
*
* // PopupLayoutConstants.js(弹窗界面,另一个文件)
* var LayoutConstants = LayoutConstants || {}; // 保护性声明
* LayoutConstants.ROUND_RESULT_LAYOUT = { ... };
* LayoutConstants.GAME_RESULT_LAYOUT = { ... };
*
* 两个文件加载后,LayoutConstants 包含所有属性。
*
* @version 1.0.0
* @see LayoutConstants.js 金仙麻将实际实现
* @see CardLayoutManager.js 使用 LayoutConstants 计算精灵坐标
*/
'use strict';
/**
* LayoutConstants - 布局常量对象
* 所有布局配置挂载在此全局对象下
*/
var LayoutConstants = {};
// ============================================================================
// 设计分辨率
// ============================================================================
/** 设计稿宽度(px) */
LayoutConstants.DESIGN_WIDTH = 1280;
/** 设计稿高度(px) */
LayoutConstants.DESIGN_HEIGHT = 720;
// ============================================================================
// 手牌区布局(示例:4人麻将)
// ============================================================================
/**
* 手牌区布局配置
*
* DIRECTION 值:
* 'horizontal' - 从左到右(PLAYER_1 常用)
* 'horizontal-reverse' - 从右到左(PLAYER_3 常用)
* 'vertical' - 从上到下(PLAYER_4 常用)
* 'vertical-reverse' - 从下到上(PLAYER_2 常用)
*
* ALIGN_MODE 值:
* 'start' - 从 START_X/Y 开始,按索引递增方向铺开
* 'center' - 手牌整体居中于 START_X/Y
* 'end' - 最后一张牌落在 START_X/Y,向前排列
*/
LayoutConstants.HAND_CARD_LAYOUT = {
// 玩家1 (下方 - 自己) - 正视角,无透视
PLAYER_1: {
START_X: 164, // 第1张牌的左边界X
START_Y: 613, // 手牌区Y坐标
CARD_WIDTH: 74, // 单张牌宽度(含视觉厚度)
CARD_HEIGHT: 102, // 单张牌高度
CARD_SPACING: -2, // 牌间距(负值=重叠)
DRAW_CARD_GAP: 15, // 摸牌与手牌的额外间隔
SELECT_OFFSET_Y: -20, // 选中牌向上偏移量
MAX_CARDS: 13, // 最大手牌数
DIRECTION: 'horizontal',
ALIGN_MODE: 'end',
PERSPECTIVE_OFFSET_X: 0,
PERSPECTIVE_OFFSET_Y: 0
},
// 玩家2 (右方) - 侧视角,竖排,透视向左上
PLAYER_2: {
START_X: 1205,
START_Y: 537,
CARD_WIDTH: 52,
CARD_HEIGHT: 72,
CARD_SPACING: 0,
DRAW_CARD_GAP: 0,
MAX_CARDS: 13,
DIRECTION: 'vertical-reverse',
ALIGN_MODE: 'center',
PERSPECTIVE_OFFSET_X: -8,
PERSPECTIVE_OFFSET_Y: -37,
DRAW_CARD_PERSPECTIVE_X: -12,
DRAW_CARD_PERSPECTIVE_Y: 18
},
// 玩家3 (上方) - 对面视角,横排,无透视
PLAYER_3: {
START_X: 820,
START_Y: 44,
CARD_WIDTH: 43,
CARD_HEIGHT: 58,
CARD_SPACING: -3,
DRAW_CARD_GAP: 6,
MAX_CARDS: 13,
DIRECTION: 'horizontal-reverse',
ALIGN_MODE: 'end',
PERSPECTIVE_OFFSET_X: 0,
PERSPECTIVE_OFFSET_Y: 0
},
// 玩家4 (左方) - 侧视角,竖排,透视向左上
PLAYER_4: {
START_X: 139,
START_Y: 74,
CARD_WIDTH: 52,
CARD_HEIGHT: 72,
CARD_SPACING: 0,
DRAW_CARD_GAP: 12,
MAX_CARDS: 13,
DIRECTION: 'vertical',
ALIGN_MODE: 'center',
PERSPECTIVE_OFFSET_X: -8,
PERSPECTIVE_OFFSET_Y: -37,
DRAW_CARD_PERSPECTIVE_X: -9,
DRAW_CARD_PERSPECTIVE_Y: -35
}
};
// ============================================================================
// 弃牌区布局(示例)
// ============================================================================
/**
* 弃牌区布局配置
*
* CARDS_PER_ROW / MAX_ROWS:弃牌按行/列分组排列(DIRECTION 为 vertical 时按列)。
* ROW_PERSPECTIVE_X/Y:每换一行的累积透视偏移,用于模拟牌桌中央的深度感。
*/
LayoutConstants.DISCARD_LAYOUT = {
PLAYER_1: {
START_X: 500,
START_Y: 345,
CARD_WIDTH: 45,
CARD_HEIGHT: 55,
CARDS_PER_ROW: 9,
ROW_SPACING: 2,
CARD_SPACING: 1,
MAX_ROWS: 3,
CARD_PERSPECTIVE_X: 0,
CARD_PERSPECTIVE_Y: 0,
ROW_PERSPECTIVE_X: 2,
ROW_PERSPECTIVE_Y: -3
},
PLAYER_2: {
START_X: 755,
START_Y: 175,
CARD_WIDTH: 28,
CARD_HEIGHT: 36,
CARDS_PER_ROW: 3,
ROW_SPACING: 1,
CARD_SPACING: 1,
MAX_ROWS: 9,
DIRECTION: 'vertical',
CARD_PERSPECTIVE_X: -1,
CARD_PERSPECTIVE_Y: 0,
ROW_PERSPECTIVE_X: -2,
ROW_PERSPECTIVE_Y: 1
},
PLAYER_3: {
START_X: 720,
START_Y: 95,
CARD_WIDTH: 36,
CARD_HEIGHT: 28,
CARDS_PER_ROW: 9,
ROW_SPACING: 2,
CARD_SPACING: 1,
MAX_ROWS: 3,
DIRECTION: 'horizontal-reverse',
CARD_PERSPECTIVE_X: 0,
CARD_PERSPECTIVE_Y: 0,
ROW_PERSPECTIVE_X: -2,
ROW_PERSPECTIVE_Y: 2
},
PLAYER_4: {
START_X: 200,
START_Y: 280,
CARD_WIDTH: 58,
CARD_HEIGHT: 41,
CARDS_PER_ROW: 3,
ROW_SPACING: 1,
CARD_SPACING: 1,
MAX_ROWS: 9,
DIRECTION: 'vertical-reverse',
CARD_PERSPECTIVE_X: 1,
CARD_PERSPECTIVE_Y: 0,
ROW_PERSPECTIVE_X: -3,
ROW_PERSPECTIVE_Y: -12
}
};
// ============================================================================
// 副露区布局(吃/碰/杠)
// ============================================================================
/**
* 副露区布局配置
*
* 透视偏移量(组内 + 组间):
* CARD_PERSPECTIVE_X/Y - 同一副露组内,每张牌相对前一张的额外偏移(累加)
* GROUP_PERSPECTIVE_X/Y - 不同副露组之间,每组相对前一组的额外偏移(累加)
*
* 坐标计算公式(CardLayoutManager.js 负责实现):
* 透视X = cardIndex × CARD_PERSPECTIVE_X + meldIndex × GROUP_PERSPECTIVE_X
* 最终X = 基准X + 透视X
*/
LayoutConstants.MELD_LAYOUT = {
PLAYER_1: {
START_X: 105,
START_Y: 639,
CARD_WIDTH: 64,
CARD_HEIGHT: 84,
CARD_SPACING: 0,
GROUP_SPACING: 8,
DIRECTION: 'horizontal',
MAX_GROUPS: 4,
KONG_STACK_OFFSET_X: 0,
KONG_STACK_OFFSET_Y: -20,
CARD_PERSPECTIVE_X: -5,
CARD_PERSPECTIVE_Y: 0,
GROUP_PERSPECTIVE_X: -3,
GROUP_PERSPECTIVE_Y: 0
},
PLAYER_2: {
START_X: 1115,
START_Y: 509,
CARD_WIDTH: 65,
CARD_HEIGHT: 48,
CARD_SPACING: 0,
GROUP_SPACING: 0,
DIRECTION: 'vertical-reverse',
MAX_GROUPS: 4,
KONG_STACK_OFFSET_X: 6,
KONG_STACK_OFFSET_Y: -20,
CARD_PERSPECTIVE_X: -7,
CARD_PERSPECTIVE_Y: 35,
GROUP_PERSPECTIVE_X: -25,
GROUP_PERSPECTIVE_Y: 35
},
PLAYER_3: {
START_X: 930,
START_Y: 48,
CARD_WIDTH: 43,
CARD_HEIGHT: 58,
CARD_SPACING: 0,
GROUP_SPACING: 4,
DIRECTION: 'horizontal-reverse',
MAX_GROUPS: 4,
KONG_STACK_OFFSET_X: 0,
KONG_STACK_OFFSET_Y: -9,
CARD_PERSPECTIVE_X: 3,
CARD_PERSPECTIVE_Y: 0,
GROUP_PERSPECTIVE_X: -3,
GROUP_PERSPECTIVE_Y: 0
},
PLAYER_4: {
START_X: 198,
START_Y: 91,
CARD_WIDTH: 63,
CARD_HEIGHT: 49,
CARD_SPACING: 0,
GROUP_SPACING: 0,
DIRECTION: 'vertical',
MAX_GROUPS: 4,
KONG_STACK_OFFSET_X: -7,
KONG_STACK_OFFSET_Y: -17,
CARD_PERSPECTIVE_X: -6,
CARD_PERSPECTIVE_Y: -30,
GROUP_PERSPECTIVE_X: -25,
GROUP_PERSPECTIVE_Y: -30
}
};
// ============================================================================
// 玩家信息 UI 布局(支持 2/3/4 人)
// ============================================================================
/**
* 玩家信息 UI 布局配置
*
* 座位映射(seatToInd):
* 不同人数局中,座位号(0-3)映射到屏幕显示位置(ind 0-3)。
* ind = -1 表示该座位在此人数局中不显示。
*
* 约定:ind0=下方(自己) ind1=右方 ind2=上方 ind3=左方
*
* 位置配置(POSITIONS[ind]):
* FRAME_* 底框精灵的绝对坐标和尺寸
* AVATAR.* 头像相对于底框左上角的偏移量(OFFSET_X/Y + WIDTH/HEIGHT)
* NAME_OFFSET_X/Y 昵称相对于头像左上角的偏移
* SCORE_OFFSET_X/Y 分数相对于头像左上角的偏移
*/
LayoutConstants.PLAYER_INFO_LAYOUT = {
DESIGN_WIDTH: 1280,
DESIGN_HEIGHT: 720,
// 座位号 → 显示位置 ind 的映射
SEAT_MAPPING: {
4: [0, 1, 2, 3],
3: [0, 1, 3, -1], // 3人局:北位(ind2)不显示
2: [0, 2, -1, -1] // 2人局:只有南北两位
},
VISIBLE_POSITIONS: {
4: [0, 1, 2, 3],
3: [0, 1, 3],
2: [0, 2]
},
POSITIONS: {
// ind=0: 南位(下方) - 玩家自己
0: {
FRAME_X: 6, FRAME_Y: 592,
FRAME_WIDTH: 90, FRAME_HEIGHT: 115,
AVATAR: {
OFFSET_X: 5, OFFSET_Y: 5,
WIDTH: 80, HEIGHT: 80,
NAME_OFFSET_X: -400, NAME_OFFSET_Y: 888,
SCORE_OFFSET_X: 0, SCORE_OFFSET_Y: 88
},
NAME_CENTER_ALIGN: true
},
// ind=1: 东位(右方)
1: {
FRAME_X: 1186, FRAME_Y: 85,
FRAME_WIDTH: 90, FRAME_HEIGHT: 115,
AVATAR: {
OFFSET_X: 5, OFFSET_Y: 5,
WIDTH: 80, HEIGHT: 80,
NAME_OFFSET_X: 905, NAME_OFFSET_Y: -888,
SCORE_OFFSET_X: 0, SCORE_OFFSET_Y: 88
},
NAME_CENTER_ALIGN: true
},
// ind=2: 北位(上方)
2: {
FRAME_X: 1006, FRAME_Y: 5,
FRAME_WIDTH: 90, FRAME_HEIGHT: 115,
AVATAR: {
OFFSET_X: 5, OFFSET_Y: 5,
WIDTH: 80, HEIGHT: 80,
NAME_OFFSET_X: 905, NAME_OFFSET_Y: -888,
SCORE_OFFSET_X: 0, SCORE_OFFSET_Y: 88
},
NAME_CENTER_ALIGN: true
},
// ind=3: 西位(左方)
3: {
FRAME_X: 7, FRAME_Y: 59,
FRAME_WIDTH: 90, FRAME_HEIGHT: 115,
AVATAR: {
OFFSET_X: 5, OFFSET_Y: 5,
WIDTH: 80, HEIGHT: 80,
NAME_OFFSET_X: -605, NAME_OFFSET_Y: -888,
SCORE_OFFSET_X: 0, SCORE_OFFSET_Y: 88
},
NAME_CENTER_ALIGN: true
}
},
TEXT_STYLE: {
NAME_FONT_SIZE: 14,
NAME_MAX_LENGTH: 6,
SCORE_FONT_SIZE: 12,
CHAR_WIDTH: 9
}
};
// ============================================================================
// 庄家标识布局
// ============================================================================
/**
* 庄家图标布局
*
* POSITION_TYPE 决定图标与头像底框的对齐方式:
* 'top-left' - 图标左上角 = 底框右上角(图标显示在底框右侧)
* 'top-right' - 图标右上角 = 底框左上角(图标显示在底框左侧)
*/
LayoutConstants.DEALER_ICON_LAYOUT = {
ICON_WIDTH: 32,
ICON_HEIGHT: 32,
SEAT_MAPPING: {
4: [0, 1, 2, 3],
3: [0, 1, 3, -1],
2: [0, 2, -1, -1]
},
POSITIONS: {
0: { POSITION_TYPE: 'top-left' },
1: { POSITION_TYPE: 'top-right' },
2: { POSITION_TYPE: 'top-right' },
3: { POSITION_TYPE: 'top-left' }
}
};
// ============================================================================
// 操作倒计时布局
// ============================================================================
/**
* 操作倒计时布局
*
* 数字图片居中显示在 CENTER_X:
* actualX = CENTER_X - (digitCount × CHAR_WIDTH / 2)
*/
LayoutConstants.COUNTDOWN_LAYOUT = {
CENTER_X: 640, // 水平居中锚点
Y: 296,
CHAR_WIDTH: 31,
CHAR_HEIGHT: 33
};
// ============================================================================
// 操作按钮布局(吃/碰/杠/胡/听/过)
// ============================================================================
/**
* 操作按钮动态水平布局
*
* 从右锚点 ANCHOR_X 向左排列,按钮数量变化时整体右对齐:
* totalW = count × BUTTON_WIDTH + (count-1) × BUTTON_SPACING
* firstX = ANCHOR_X - totalW
* nthX = firstX + (n-1) × (BUTTON_WIDTH + BUTTON_SPACING)
*/
LayoutConstants.ACTION_BUTTON_LAYOUT = {
ANCHOR_X: 1180, // 右对齐锚点
Y: 560, // 按钮Y坐标(统一)
BUTTON_WIDTH: 100,
BUTTON_HEIGHT: 80,
BUTTON_SPACING: 10
};
// ============================================================================
// 最后出牌指示器布局
// ============================================================================
/**
* 最后出牌指示器偏移量(相对于该牌的坐标)
*/
LayoutConstants.LAST_DISCARD_INDICATOR_LAYOUT = {
OFFSET: {
0: { X: 8, Y: -20 },
1: { X: 15, Y: -25 },
2: { X: 6, Y: -20 },
3: { X: 15, Y: -23 }
}
};