Commit 83aad560 authored by 董政锦's avatar 董政锦

Merge branch 'game3-PC马数据和位置问题、开始游戏时的闪灰底问题;' into 'main'

fix: game3 游戏页闪灰底问题; See merge request !14
parents 3ad7c03b c7cf6e57
...@@ -440,43 +440,40 @@ const rankCloseHandler = () => { ...@@ -440,43 +440,40 @@ const rankCloseHandler = () => {
</template> </template>
<style scoped> <style scoped>
.rule-container { .rule-container {
.rule-txt-container { .rule-txt-container {
background: v-bind("imageUrls.rule") center / cover no-repeat; background: v-bind('imageUrls.rule') center / cover no-repeat;
width: 692px; width: 692px;
height: 797px; height: 797px;
margin-top: -20%; margin-top: -20%;
transform: scale(0.5); transform: scale(0.5);
.rule-title { .rule-title {
text-align: center; text-align: center;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-size: 26pt; font-size: 26pt;
line-height: 50pt; line-height: 50pt;
letter-spacing: 4px; letter-spacing: 4px;
} }
.rule-close { .rule-txt {
width: 57px; font-size: 26pt;
height: 57px; padding: 40px;
background: v-bind('imageUrls.close') center / cover no-repeat; margin-top: 10px;
transform: translateX(-50%) scale(1.5); color: #AA0000;
position: absolute; }
top: -73px;
left: 100%;
margin-left: -48px;
}
}
.rule-close { .rule-close {
width: 57px; width: 57px;
height: 57px; height: 57px;
background: v-bind("imageUrls.close") center / cover no-repeat; background: v-bind('imageUrls.close') center / cover no-repeat;
transform: translateX(-50%) scale(1.4); transform: translateX(-50%) scale(1.5);
position: absolute; position: absolute;
bottom: -75px; top: -73px;
left: 50%; left: 100%;
} margin-left: -48px;
}
}
} }
.ranklist-container { .ranklist-container {
......
...@@ -86,6 +86,34 @@ export function $is_run_local(): boolean { ...@@ -86,6 +86,34 @@ export function $is_run_local(): boolean {
return false; return false;
} }
const LOCAL_MODE_KEY = 'fc6_local_mode';
/** 获取当前模式:localStorage 优先 > 自动检测 */
export function $is_local_mode(): boolean {
const stored = localStorage.getItem(LOCAL_MODE_KEY);
if (stored === '1') return true;
if (stored === '0') return false;
return $is_run_local();
}
/** 设置模式:'1'=本地模拟, '0'=线上WS, 其他=清除恢复自动 */
export function $set_local_mode(val: string) {
if (val === '1' || val === '0') {
localStorage.setItem(LOCAL_MODE_KEY, val);
} else {
localStorage.removeItem(LOCAL_MODE_KEY);
}
location.reload();
}
// 暴露到 window 方便开发调试
if (typeof window !== 'undefined') {
(window as any).__fc6LocalMode = (val?: string) => {
if (val !== undefined) $set_local_mode(val);
return $is_local_mode() ? '本地模拟' : '线上WS';
};
}
export function $query(q: string | string[]): string | string[] { export function $query(q: string | string[]): string | string[] {
const query = location.href.match(/\?([^#]*)/)?.[1]?.replace(/\/$/, ''); const query = location.href.match(/\?([^#]*)/)?.[1]?.replace(/\/$/, '');
const params = new URLSearchParams(query); const params = new URLSearchParams(query);
......
...@@ -145,7 +145,8 @@ onBeforeUnmount(() => { ...@@ -145,7 +145,8 @@ onBeforeUnmount(() => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-color: rgba(0, 0, 0, 0.2); /* 用径向渐变:只在数字周围微暗,四角完全透明 → 保留背景图可见 */
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.15) 0%, transparent 70%);
pointer-events: none; pointer-events: none;
} }
......
...@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue' ...@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue'
import { useAdminGameSocket } from '@/composables/useAdminGameSocket' import { useAdminGameSocket } from '@/composables/useAdminGameSocket'
import type Player from '@/commons/player.ts' import type Player from '@/commons/player.ts'
import { playApplauseMusic, playGame6Music, stopAllMusic, stopGame6Music } from '@/commons/music' import { playApplauseMusic, playGame6Music, stopAllMusic, stopGame6Music } from '@/commons/music'
import { $confirm, $remove_socket_storage, $notify, $toast } from '@/commons/utils.ts' import { $confirm, $is_local_mode, $remove_socket_storage, $notify, $toast } from '@/commons/utils.ts'
const router = useRouter() const router = useRouter()
...@@ -231,8 +231,12 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({ ...@@ -231,8 +231,12 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
}) })
const startGameWithMusic = () => { const startGameWithMusic = () => {
// 本地调试模式:跳过 WebSocket,直接进入赛马画面
if ($is_local_mode()) {
gotoRank1WithIntro()
return
}
if (playerCount.value == 0) { if (playerCount.value == 0) {
// alert('房间中没有玩家')
$toast('房间中没有玩家'); $toast('房间中没有玩家');
return; return;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { ref, watch, onMounted, onUnmounted } from 'vue' import { ref, watch, onMounted, onUnmounted } from 'vue'
import DesignStage from '../../../components/DesignStage.vue' import DesignStage from '../../../components/DesignStage.vue'
import type Player from '../../../commons/player.ts' import type Player from '../../../commons/player.ts'
import { $format_str, $is_run_local } from '@/commons/utils.ts' import { $format_str, $is_local_mode } from '@/commons/utils.ts'
import { assetUrl, cssAssetUrl } from '@/commons/assets.ts' import { assetUrl, cssAssetUrl } from '@/commons/assets.ts'
import { playApplauseMusic, stopApplauseMusic } from '@/commons/music' import { playApplauseMusic, stopApplauseMusic } from '@/commons/music'
...@@ -44,7 +44,7 @@ const imageUrls = { ...@@ -44,7 +44,7 @@ const imageUrls = {
const HORSE_SIZE = 280; const HORSE_SIZE = 280;
const HORSE_START_X = -HORSE_SIZE; const HORSE_START_X = -HORSE_SIZE;
const HORSE_END_X = 1920; const HORSE_END_X = 1920;
const HORSE_Y_OFFSET = 80; // 马匹整体向下偏移,补偿帧图片中马匹偏上的空白区域 const HORSE_Y_OFFSET = 50; // 马匹整体偏移
const horseFrameConfigs = [ const horseFrameConfigs = [
{ type: 1, dir: 'game3/ma1', prefix: '1ma_', startIndex: 24, count: 12 }, { type: 1, dir: 'game3/ma1', prefix: '1ma_', startIndex: 24, count: 12 },
...@@ -127,9 +127,11 @@ const detectScoreChanges = (players: Player[]) => { ...@@ -127,9 +127,11 @@ const detectScoreChanges = (players: Player[]) => {
if (newScore > prev) { if (newScore > prev) {
const current = updated[key] || 0; const current = updated[key] || 0;
updated[key] = Math.min(current + BOOST_PULSE, MAX_BOOST); // boost 量与分数增量成正比,让领先者拉得更快,制造视觉差距
const delta = newScore - prev;
updated[key] = Math.min(current + BOOST_PULSE * Math.max(delta, 1), MAX_BOOST);
hasNewBoost = true; hasNewBoost = true;
console.log(`[game3 Rank1View] 🚀 ${p.nickname} score ${prev}${newScore}, boost: ${updated[key].toFixed(1)}`); console.log(`[game3 Rank1View] 🚀 ${p.nickname} score ${prev}${newScore}${delta}), boost: ${updated[key].toFixed(1)}`);
} }
prevScores[key] = newScore; prevScores[key] = newScore;
...@@ -143,6 +145,8 @@ const detectScoreChanges = (players: Player[]) => { ...@@ -143,6 +145,8 @@ const detectScoreChanges = (players: Player[]) => {
watch( watch(
() => props.rankList, () => props.rankList,
(rankList) => { (rankList) => {
// 本地模式:无 WS 数据时不覆盖本地模拟数据
if ($is_local_mode() && (!rankList || rankList.length === 0)) return;
console.log('[game3 Rank1View] websocket rankList 数据:', JSON.parse(JSON.stringify(rankList?.slice(0, 3)))); console.log('[game3 Rank1View] websocket rankList 数据:', JSON.parse(JSON.stringify(rankList?.slice(0, 3))));
updateRankList(rankList); updateRankList(rankList);
// 检测积分增长 → 触发瞬时加速脉冲(对齐 game4) // 检测积分增长 → 触发瞬时加速脉冲(对齐 game4)
...@@ -276,7 +280,7 @@ const stopHorseLoop = () => { ...@@ -276,7 +280,7 @@ const stopHorseLoop = () => {
}; };
// ========== 生成/更新马匹(对齐 game4 updateHorses 模式,保留已有马匹进度) ========== // ========== 生成/更新马匹(对齐 game4 updateHorses 模式,保留已有马匹进度) ==========
const LANE_TOP_POSITIONS = [480, 640, 800]; const LANE_TOP_POSITIONS = [310, 510, 720];
const updateHorses = (players: Player[]) => { const updateHorses = (players: Player[]) => {
const validPlayers = players.filter(p => { const validPlayers = players.filter(p => {
...@@ -340,7 +344,6 @@ const startIntro = async () => { ...@@ -340,7 +344,6 @@ const startIntro = async () => {
// 重置加速状态 // 重置加速状态
transientBoosts.value = {}; transientBoosts.value = {};
Object.keys(prevScores).forEach(k => delete prevScores[k]); Object.keys(prevScores).forEach(k => delete prevScores[k]);
stopApplauseMusic();
preloadHorseFrames(); preloadHorseFrames();
const countdownPromise = designStage.value?.startCountdown(3, 60); const countdownPromise = designStage.value?.startCountdown(3, 60);
...@@ -386,31 +389,56 @@ const renderAvatar = (item: any | null) => { ...@@ -386,31 +389,56 @@ const renderAvatar = (item: any | null) => {
onMounted(() => { onMounted(() => {
preloadHorseFrames(); preloadHorseFrames();
if ($is_run_local()) { if ($is_local_mode()) {
// 本地模式:用模拟玩家填充 list,驱动马匹渲染 // 本地模式:预填10个模拟玩家数据,由 startIntro 流程驱动赛马
const mockAvatars = [
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Felix',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Aneka',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Salem',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Midnight',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Lucky',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Bandit',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Misty',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Pepper',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Oreo',
'https://api.dicebear.com/9.x/fun-emoji/svg?seed=Tiger',
];
list.value = [ list.value = [
{ userid: 'local-1', nickname: '骑手一', avatar: '', score: 45 }, { userid: 'local-1', nickname: '疾风骑士', avatar: mockAvatars[0], score: 45 },
{ userid: 'local-2', nickname: '骑手二', avatar: '', score: 38 }, { userid: 'local-2', nickname: '闪电侠', avatar: mockAvatars[1], score: 38 },
{ userid: 'local-3', nickname: '骑手三', avatar: '', score: 30 }, { userid: 'local-3', nickname: '追风者', avatar: mockAvatars[2], score: 30 },
{ userid: 'local-4', nickname: '骑手四', avatar: '', score: 22 }, { userid: 'local-4', nickname: '千里马', avatar: mockAvatars[3], score: 22 },
{ userid: 'local-5', nickname: '骑手五', avatar: '', score: 15 }, { userid: 'local-5', nickname: '飞毛腿', avatar: mockAvatars[4], score: 15 },
{ userid: 'local-6', nickname: '烈焰驹', avatar: mockAvatars[5], score: 52 },
{ userid: 'local-7', nickname: '暴风战士', avatar: mockAvatars[6], score: 41 },
{ userid: 'local-8', nickname: '神行者', avatar: mockAvatars[7], score: 35 },
{ userid: 'local-9', nickname: '银鞍客', avatar: mockAvatars[8], score: 28 },
{ userid: 'local-10', nickname: '踏雪无痕', avatar: mockAvatars[9], score: 18 },
]; ];
gameStarted.value = true; // 每匹马不同的基础增速:领先者(高分)增速更高,自然拉开差距
isGameRunning.value = true; const growthRates: Record<string, number> = {};
updateHorses(list.value); list.value.forEach((p, i) => {
startHorseLoop(); // 高分马增速 1.4~1.8x,低分马 0.6~1.0x,制造持久速度差异
// 模拟分数变化 → 驱动 detectScoreChanges growthRates[p.userid!] = 0.4 + (i / (list.value.length - 1)) * 1.4;
let simTick = 0; });
const simInterval = setInterval(() => { // 跟随 isGameRunning 自动管理模拟分数组件
if (!isGameRunning.value) { clearInterval(simInterval); return; } let simInterval: ReturnType<typeof setInterval> | undefined;
simTick++; watch(isGameRunning, (running) => {
for (const p of list.value) { if (simInterval) { clearInterval(simInterval); simInterval = undefined; }
if (typeof p.score === 'number') { if (running) {
p.score += Math.floor(Math.random() * 3); simInterval = setInterval(() => {
} if (!isGameRunning.value) { clearInterval(simInterval); simInterval = undefined; return; }
for (const p of list.value) {
if (typeof p.score === 'number' && p.score < 120 && p.userid) {
const rate = growthRates[p.userid] || 1.0;
const growth = Math.max(1, Math.floor((Math.random() * 3 + 1) * rate));
p.score += growth;
}
}
detectScoreChanges(list.value);
}, 1500);
} }
detectScoreChanges(list.value); }, { immediate: true });
}, 1500);
} }
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment