提交 7dd0cad2 authored 作者: lidongxu's avatar lidongxu

大方向是对了

上级 7b6d9a2c
...@@ -6,7 +6,7 @@ import { getAllPlayerStates, clearGameState, getCurrentRoom, getPlayerTeam, getC ...@@ -6,7 +6,7 @@ import { getAllPlayerStates, clearGameState, getCurrentRoom, getPlayerTeam, getC
import { initSocket, getConnectionStatus } from './socket.js' import { initSocket, getConnectionStatus } from './socket.js'
import { drawBackground } from './renderer/background.js' import { drawBackground } from './renderer/background.js'
import { drawBubbleGrid } from './renderer/bubbleGrid.js' import { drawBubbleGrid } from './renderer/bubbleGrid.js'
import { drawBubble3D, BUBBLE_RADIUS } from './renderer/bubble.js' import { drawBubble3D, getBubbleRadius, configureBubbleLayout } from './renderer/bubble.js'
import { drawShooter } from './renderer/shooter.js' import { drawShooter } from './renderer/shooter.js'
import { drawGameInfo, drawTeamResultOverlay } from './renderer/gameinfo.js' import { drawGameInfo, drawTeamResultOverlay } from './renderer/gameinfo.js'
import { detectAndCreateBursts, updateAndDrawBursts, clearPrevGrid } from './renderer/explosion.js' import { detectAndCreateBursts, updateAndDrawBursts, clearPrevGrid } from './renderer/explosion.js'
...@@ -42,6 +42,12 @@ function applyScaler(playerCount = 1) { ...@@ -42,6 +42,12 @@ function applyScaler(playerCount = 1) {
function renderPlayer(state, offsetX, roomId) { function renderPlayer(state, offsetX, roomId) {
const pid = state.playerId ?? 1 const pid = state.playerId ?? 1
// 从 grid 数据推算偶数行列数,动态调整泡泡大小
// 偶数行(row 0)的列数决定了整个网格的列配置
if (state.grid && state.grid.length && state.grid[0]) {
configureBubbleLayout(state.grid[0].length)
}
// 初始化该玩家的碎裂效果列表 // 初始化该玩家的碎裂效果列表
if (!playerBursts.has(pid)) playerBursts.set(pid, []) if (!playerBursts.has(pid)) playerBursts.set(pid, [])
const bursts = playerBursts.get(pid) const bursts = playerBursts.get(pid)
...@@ -58,7 +64,7 @@ function renderPlayer(state, offsetX, roomId) { ...@@ -58,7 +64,7 @@ function renderPlayer(state, offsetX, roomId) {
if (state.fireBubbles && state.fireBubbles.length) { if (state.fireBubbles && state.fireBubbles.length) {
for (const fb of state.fireBubbles) { for (const fb of state.fireBubbles) {
if (fb.active !== false) { if (fb.active !== false) {
drawBubble3D(ctx, fb.x, fb.y, BUBBLE_RADIUS, fb.color || 1) drawBubble3D(ctx, fb.x, fb.y, getBubbleRadius(), fb.color || 1)
} }
} }
} }
......
...@@ -4,9 +4,31 @@ ...@@ -4,9 +4,31 @@
*/ */
import { SCREEN_WIDTH, SAFE_AREA_TOP } from '../constants.js' import { SCREEN_WIDTH, SAFE_AREA_TOP } from '../constants.js'
// 11 个泡泡平铺满屏幕宽度:SCREEN_WIDTH = 11 × 2R = 22R // ─── 动态列数适配 ────────────────────────────────────────────
export const BUBBLE_RADIUS = SCREEN_WIDTH / 22 // 默认 11 列(手机端),iPad 端可能 14~15 列
const ROW_HEIGHT = BUBBLE_RADIUS * Math.sqrt(3) // 大屏端从收到的 grid 数据推算列数,动态调整泡泡大小
export let BUBBLE_RADIUS = SCREEN_WIDTH / 22
let ROW_HEIGHT = BUBBLE_RADIUS * Math.sqrt(3)
let GRID_OFFSET_X = 0
let _evenCols = 11
/**
* 根据偶数行列数重新计算泡泡大小和网格偏移
* 在每帧渲染前调用,从 grid 数据推算列数
* @param {number} evenCols 偶数行列数
*/
export function configureBubbleLayout(evenCols) {
if (evenCols === _evenCols) return
_evenCols = evenCols
BUBBLE_RADIUS = SCREEN_WIDTH / (evenCols * 2)
ROW_HEIGHT = BUBBLE_RADIUS * Math.sqrt(3)
GRID_OFFSET_X = (SCREEN_WIDTH - evenCols * 2 * BUBBLE_RADIUS) / 2
}
/** 获取当前泡泡半径(供需要实时值的模块使用) */
export function getBubbleRadius() { return BUBBLE_RADIUS }
/** 获取当前网格偏移 */
export function getGridOffsetX() { return GRID_OFFSET_X }
/** /**
* 9 种泡泡颜色(颜色索引 1-9),用于爆炸粒子效果取色。 * 9 种泡泡颜色(颜色索引 1-9),用于爆炸粒子效果取色。
...@@ -102,7 +124,7 @@ export function getActiveColorCount(score) { ...@@ -102,7 +124,7 @@ export function getActiveColorCount(score) {
*/ */
export function gridToScreen(row, col) { export function gridToScreen(row, col) {
const R = BUBBLE_RADIUS const R = BUBBLE_RADIUS
const x = row % 2 === 0 ? col * 2 * R + R : col * 2 * R + 2 * R const x = GRID_OFFSET_X + (row % 2 === 0 ? col * 2 * R + R : col * 2 * R + 2 * R)
const y = row * ROW_HEIGHT + R + SAFE_AREA_TOP const y = ROW_HEIGHT * row + R + SAFE_AREA_TOP
return { x, y } return { x, y }
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* 泡泡网格渲染(移植自 minigame-1) * 泡泡网格渲染(移植自 minigame-1)
* 根据 state 中的 grid 二维数组和 pushAnimOffsetY 绘制网格泡泡 * 根据 state 中的 grid 二维数组和 pushAnimOffsetY 绘制网格泡泡
*/ */
import { BUBBLE_RADIUS, gridToScreen, drawBubble3D } from './bubble.js' import { getBubbleRadius, gridToScreen, drawBubble3D } from './bubble.js'
/** /**
* 绘制泡泡网格 * 绘制泡泡网格
...@@ -20,7 +20,7 @@ export function drawBubbleGrid(ctx, grid, pushAnimOffsetY = 0) { ...@@ -20,7 +20,7 @@ export function drawBubbleGrid(ctx, grid, pushAnimOffsetY = 0) {
const color = rowArr[col] const color = rowArr[col]
if (!color) continue if (!color) continue
const { x, y } = gridToScreen(row, col) const { x, y } = gridToScreen(row, col)
drawBubble3D(ctx, x, y + offsetY, BUBBLE_RADIUS, color) drawBubble3D(ctx, x, y + offsetY, getBubbleRadius(), color)
} }
} }
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* 动画过程:闪光 → 碎片四散 → 渐隐消失,只播放一次。 * 动画过程:闪光 → 碎片四散 → 渐隐消失,只播放一次。
*/ */
import { BUBBLE_RADIUS, BUBBLE_COLORS, gridToScreen } from './bubble.js' import { getBubbleRadius, BUBBLE_COLORS, gridToScreen } from './bubble.js'
// ─── 碎片粒子 ───────────────────────────────────────────────────────────────── // ─── 碎片粒子 ─────────────────────────────────────────────────────────────────
...@@ -68,8 +68,8 @@ class Ring { ...@@ -68,8 +68,8 @@ class Ring {
this.x = x this.x = x
this.y = y this.y = y
this.color = color this.color = color
this.r = BUBBLE_RADIUS * 0.3 this.r = getBubbleRadius() * 0.3
this.maxR = BUBBLE_RADIUS * 2.2 this.maxR = getBubbleRadius() * 2.2
this.life = 0 this.life = 0
this.maxLife = 40 this.maxLife = 40
} }
...@@ -81,7 +81,7 @@ class Ring { ...@@ -81,7 +81,7 @@ class Ring {
update() { update() {
this.life++ this.life++
const t = this.life / this.maxLife const t = this.life / this.maxLife
this.r = BUBBLE_RADIUS * 0.3 + (this.maxR - BUBBLE_RADIUS * 0.3) * t this.r = getBubbleRadius() * 0.3 + (this.maxR - getBubbleRadius() * 0.3) * t
this.alpha = 0.6 * (1 - t) this.alpha = 0.6 * (1 - t)
} }
...@@ -107,7 +107,7 @@ class BubbleBurst { ...@@ -107,7 +107,7 @@ class BubbleBurst {
this._y = y this._y = y
const colorHex = BUBBLE_COLORS[colorIdx] || '#ffffff' const colorHex = BUBBLE_COLORS[colorIdx] || '#ffffff'
const R = BUBBLE_RADIUS const R = getBubbleRadius()
this.flashAlpha = 1.0 this.flashAlpha = 1.0
this.ring = new Ring(x, y, colorHex) this.ring = new Ring(x, y, colorHex)
...@@ -156,7 +156,7 @@ class BubbleBurst { ...@@ -156,7 +156,7 @@ class BubbleBurst {
ctx.globalAlpha = this.flashAlpha * 0.7 ctx.globalAlpha = this.flashAlpha * 0.7
ctx.fillStyle = '#ffffff' ctx.fillStyle = '#ffffff'
ctx.beginPath() ctx.beginPath()
ctx.arc(this._x, this._y, BUBBLE_RADIUS * 1.3, 0, Math.PI * 2) ctx.arc(this._x, this._y, getBubbleRadius() * 1.3, 0, Math.PI * 2)
ctx.fill() ctx.fill()
ctx.restore() ctx.restore()
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* 三种状态:未连接、已连接无房间、已连接有房间等待游戏 * 三种状态:未连接、已连接无房间、已连接有房间等待游戏
*/ */
import { SCREEN_WIDTH, SCREEN_HEIGHT } from '../constants.js' import { SCREEN_WIDTH, SCREEN_HEIGHT } from '../constants.js'
import { drawBubble3D, BUBBLE_RADIUS } from './bubble.js' import { drawBubble3D, getBubbleRadius } from './bubble.js'
let _frame = 0 let _frame = 0
...@@ -26,7 +26,7 @@ function rr(ctx, x, y, w, h, r) { ...@@ -26,7 +26,7 @@ function rr(ctx, x, y, w, h, r) {
// ─── 装饰泡泡(顶部三行漂浮泡泡)──────────────────────────────────────────────── // ─── 装饰泡泡(顶部三行漂浮泡泡)────────────────────────────────────────────────
const _decorBubbles = (() => { const _decorBubbles = (() => {
const R = BUBBLE_RADIUS const R = getBubbleRadius()
const list = [] const list = []
let sx = 0.456 let sx = 0.456
const rand = () => { sx = (sx * 9301 + 49297) % 233280; return sx / 233280 } const rand = () => { sx = (sx * 9301 + 49297) % 233280; return sx / 233280 }
...@@ -47,7 +47,7 @@ function drawDecorBubbles(ctx) { ...@@ -47,7 +47,7 @@ function drawDecorBubbles(ctx) {
ctx.globalAlpha = 0.55 ctx.globalAlpha = 0.55
for (const b of _decorBubbles) { for (const b of _decorBubbles) {
const dy = Math.sin(_frame * 0.018 + b.phase) * 3 const dy = Math.sin(_frame * 0.018 + b.phase) * 3
drawBubble3D(ctx, b.x, b.y + dy, BUBBLE_RADIUS, b.color) drawBubble3D(ctx, b.x, b.y + dy, getBubbleRadius(), b.color)
} }
ctx.restore() ctx.restore()
} }
...@@ -344,11 +344,11 @@ export function drawIdleScreen(ctx, width, height, roomId, connStatus, screenNam ...@@ -344,11 +344,11 @@ export function drawIdleScreen(ctx, width, height, roomId, connStatus, screenNam
drawDecorBubbles(ctx) drawDecorBubbles(ctx)
// 顶部遮罩(让泡泡看起来更像背景) // 顶部遮罩(让泡泡看起来更像背景)
const topMask = ctx.createLinearGradient(0, 0, 0, BUBBLE_RADIUS * 4) const topMask = ctx.createLinearGradient(0, 0, 0, getBubbleRadius() * 4)
topMask.addColorStop(0, 'rgba(30,12,60,0)') topMask.addColorStop(0, 'rgba(30,12,60,0)')
topMask.addColorStop(1, 'rgba(30,12,60,0.55)') topMask.addColorStop(1, 'rgba(30,12,60,0.55)')
ctx.fillStyle = topMask ctx.fillStyle = topMask
ctx.fillRect(0, 0, width, BUBBLE_RADIUS * 4) ctx.fillRect(0, 0, width, getBubbleRadius() * 4)
// 浮动粒子 // 浮动粒子
drawParticles(ctx, width, height) drawParticles(ctx, width, height)
......
...@@ -3,15 +3,8 @@ ...@@ -3,15 +3,8 @@
* 鸡身体朝向跟随瞄准方向倾斜 + 呼吸浮动 + 弹跳动画 * 鸡身体朝向跟随瞄准方向倾斜 + 呼吸浮动 + 弹跳动画
*/ */
import { SCREEN_WIDTH, SCREEN_HEIGHT, SAFE_AREA_BOTTOM } from '../constants.js' import { SCREEN_WIDTH, SCREEN_HEIGHT, SAFE_AREA_BOTTOM } from '../constants.js'
import { BUBBLE_RADIUS, drawBubble3D } from './bubble.js' import { getBubbleRadius, drawBubble3D } from './bubble.js'
// 鸡的尺寸
const CHICKEN_WIDTH = BUBBLE_RADIUS * 5
const CHICKEN_HEIGHT = BUBBLE_RADIUS * 6
const CHICKEN_BOTTOM_MARGIN = BUBBLE_RADIUS * 0.8
const BUBBLE_TOP_OFFSET = BUBBLE_RADIUS * 0.4
const NEXT_X_OFFSET = BUBBLE_RADIUS * 3.2
const NEXT_Y_OFFSET = BUBBLE_RADIUS * 0.5
const NEXT_SCALE = 0.65 const NEXT_SCALE = 0.65
// 鸡朝向参数 // 鸡朝向参数
...@@ -20,7 +13,6 @@ const MAX_ANGLE = 165 * Math.PI / 180 ...@@ -20,7 +13,6 @@ const MAX_ANGLE = 165 * Math.PI / 180
const MAX_LEAN_ANGLE = 25 * Math.PI / 180 const MAX_LEAN_ANGLE = 25 * Math.PI / 180
const LEAN_LERP = 0.15 const LEAN_LERP = 0.15
const BOB_SPEED = 0.03 const BOB_SPEED = 0.03
const BOB_AMOUNT = BUBBLE_RADIUS * 0.15
// 加载鸡图片 // 加载鸡图片
const _chickenImg = new Image() const _chickenImg = new Image()
...@@ -31,7 +23,7 @@ let _currentLean = 0 ...@@ -31,7 +23,7 @@ let _currentLean = 0
let _bobPhase = 0 let _bobPhase = 0
function calcAimPoints(originX, originY, angle) { function calcAimPoints(originX, originY, angle) {
const R = BUBBLE_RADIUS const R = getBubbleRadius()
const points = [{ x: originX, y: originY }] const points = [{ x: originX, y: originY }]
let cx = originX, cy = originY let cx = originX, cy = originY
let vx = Math.cos(angle), vy = -Math.sin(angle) let vx = Math.cos(angle), vy = -Math.sin(angle)
...@@ -76,10 +68,17 @@ function drawAimLine(ctx, aimPoints) { ...@@ -76,10 +68,17 @@ function drawAimLine(ctx, aimPoints) {
export function drawShooter(ctx, shooterState) { export function drawShooter(ctx, shooterState) {
if (!shooterState) return if (!shooterState) return
const R = getBubbleRadius()
const CHICKEN_WIDTH = R * 5
const CHICKEN_HEIGHT = R * 6
const CHICKEN_BOTTOM_MARGIN = R * 0.8
const BUBBLE_TOP_OFFSET = R * 0.4
const NEXT_X_OFFSET = R * 3.2
const NEXT_Y_OFFSET = R * 0.5
const x = SCREEN_WIDTH / 2 const x = SCREEN_WIDTH / 2
const chickenBottom = SCREEN_HEIGHT - SAFE_AREA_BOTTOM - CHICKEN_BOTTOM_MARGIN const chickenBottom = SCREEN_HEIGHT - SAFE_AREA_BOTTOM - CHICKEN_BOTTOM_MARGIN
const bubbleBaseY = chickenBottom - CHICKEN_HEIGHT - BUBBLE_TOP_OFFSET const bubbleBaseY = chickenBottom - CHICKEN_HEIGHT - BUBBLE_TOP_OFFSET
const R = BUBBLE_RADIUS
const aimAngle = shooterState.aimAngle ?? Math.PI / 2 const aimAngle = shooterState.aimAngle ?? Math.PI / 2
const isAiming = shooterState.isAiming ?? false const isAiming = shooterState.isAiming ?? false
...@@ -100,7 +99,7 @@ export function drawShooter(ctx, shooterState) { ...@@ -100,7 +99,7 @@ export function drawShooter(ctx, shooterState) {
// 呼吸浮动 // 呼吸浮动
_bobPhase += BOB_SPEED _bobPhase += BOB_SPEED
if (_bobPhase > Math.PI * 2) _bobPhase -= Math.PI * 2 if (_bobPhase > Math.PI * 2) _bobPhase -= Math.PI * 2
const bobOffset = Math.sin(_bobPhase) * BOB_AMOUNT const bobOffset = Math.sin(_bobPhase) * R * 0.15
// 瞄准线 // 瞄准线
const aimPoints = shooterState.aimPoints ?? (isAiming ? calcAimPoints(x, bubbleBaseY, aimAngle) : []) const aimPoints = shooterState.aimPoints ?? (isAiming ? calcAimPoints(x, bubbleBaseY, aimAngle) : [])
......
minigame-1 @ 6fe51eaf
Subproject commit 8ab3c7c4b7f885eea0ef9c4d9f51f3fa08188665 Subproject commit 6fe51eaf29c8e38ac9804b5991a841aa88ea235f
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论