Commit cf46d9ce authored by 董政锦's avatar 董政锦

fix: game4 和 game5 完成;

parent b64c8408
...@@ -176,14 +176,12 @@ onBeforeUnmount(() => { ...@@ -176,14 +176,12 @@ onBeforeUnmount(() => {
} }
}) })
// 点击马触发:iOS 先请求权限 + 触发射分 // 点击马触发:iOS 先请求权限(不触发得分)
const onTapHorse = async () => { const onTapHorse = async () => {
if (!motionPermissionGranted) { if (!motionPermissionGranted) {
await requestMotionPermission() await requestMotionPermission()
// 权限请求完成(无论结果)隐藏 iOS 提示
showIOSHint.value = false showIOSHint.value = false
} }
emit('touch')
} }
</script> </script>
......
...@@ -245,7 +245,7 @@ const startGameLoop = () => { ...@@ -245,7 +245,7 @@ const startGameLoop = () => {
return; return;
} }
if (!gameLastTs) gameLastTs = ts; if (!gameLastTs) gameLastTs = ts;
const dt = Math.min(ts - gameLastTs, 50); const dt = Math.min(ts - gameLastTs, 200);
gameLastTs = ts; gameLastTs = ts;
const newTransientBoosts: Record<string, number> = {}; const newTransientBoosts: Record<string, number> = {};
...@@ -263,7 +263,7 @@ const startGameLoop = () => { ...@@ -263,7 +263,7 @@ const startGameLoop = () => {
); );
horse.frameTimer += dt; horse.frameTimer += dt;
if (horse.frameTimer >= HORSE_FRAME_TICK_MS) { while (horse.frameTimer >= HORSE_FRAME_TICK_MS) {
horse.frameTimer -= HORSE_FRAME_TICK_MS; horse.frameTimer -= HORSE_FRAME_TICK_MS;
horse.frameIndex = (horse.frameIndex + 1) % HORSE_FRAME_COUNT; horse.frameIndex = (horse.frameIndex + 1) % HORSE_FRAME_COUNT;
} }
...@@ -474,7 +474,9 @@ defineExpose({ ...@@ -474,7 +474,9 @@ defineExpose({
transform: `translateX(${horseTranslateX(horse.xPercent)}px)`, transform: `translateX(${horseTranslateX(horse.xPercent)}px)`,
}" }"
> >
<div class="horse" :style="horseSpriteStyle(horse)"></div> <div class="horse" :style="horseSpriteStyle(horse)">
<div class="horse-shadow"></div>
</div>
<div class="horse-username"> <div class="horse-username">
<span class="username-text">{{ horse.nickname }}</span> <span class="username-text">{{ horse.nickname }}</span>
</div> </div>
...@@ -619,10 +621,21 @@ defineExpose({ ...@@ -619,10 +621,21 @@ defineExpose({
.horse { .horse {
width: 120px; width: 120px;
height: 109px; height: 109px;
background: url("@/assets/images/game4/horse.png") -17px 0 / 1472px 109px background-repeat: no-repeat;
no-repeat; position: relative;
animation: horse-run 0.6s steps(12) infinite; }
filter: drop-shadow(4px 6px 6px rgba(0, 0, 0, 0.5));
.horse-shadow {
position: absolute;
bottom: -2px;
left: 50%;
transform: translateX(-50%);
width: 120px;
height: 18px;
background: radial-gradient(ellipse, rgba(0, 0, 0, 0.45) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
z-index: 0;
} }
.horse-username { .horse-username {
...@@ -633,7 +646,7 @@ defineExpose({ ...@@ -633,7 +646,7 @@ defineExpose({
height: 40px; height: 40px;
position: absolute; position: absolute;
top: 10px; top: 10px;
left: -24px; left: -22px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -649,6 +662,8 @@ defineExpose({ ...@@ -649,6 +662,8 @@ defineExpose({
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
padding-left: 4px;
box-sizing: border-box;
} }
.horse-avatar { .horse-avatar {
...@@ -658,7 +673,7 @@ defineExpose({ ...@@ -658,7 +673,7 @@ defineExpose({
height: 30px; height: 30px;
position: absolute; position: absolute;
top: -4px; top: -4px;
left: -56px; left: -22px;
border-radius: 50%; border-radius: 50%;
} }
...@@ -671,14 +686,6 @@ defineExpose({ ...@@ -671,14 +686,6 @@ defineExpose({
} }
} }
@keyframes horse-run {
0% {
background-position: -17px 0;
}
100% {
background-position: -1457px 0;
}
}
/* ========== 结果弹窗 ========== */ /* ========== 结果弹窗 ========== */
.result-overlay { .result-overlay {
......
...@@ -39,10 +39,7 @@ const HORSE_FRAME_COUNT = 21; // 每匹马 21 帧(00-20) ...@@ -39,10 +39,7 @@ const HORSE_FRAME_COUNT = 21; // 每匹马 21 帧(00-20)
const HORSE_WIDTH = 200; const HORSE_WIDTH = 200;
const HORSE_START_X = -330; // 左侧屏外起点 const HORSE_START_X = -330; // 左侧屏外起点
const HORSE_END_X = 1920 - HORSE_WIDTH; // 右边缘贴齐屏幕右侧(1720px) const HORSE_END_X = 1920 - HORSE_WIDTH; // 右边缘贴齐屏幕右侧(1720px)
const HORSE_RUN_DURATION_SEC = 60;
const horseRunDuration = `${HORSE_RUN_DURATION_SEC}s`;
const horseStartX = `${HORSE_START_X}px`;
const horseEndX = `${HORSE_END_X}px`;
// 预构建四种马的帧 URL(与 H5 PlayingView 一致,避免运行时拼路径) // 预构建四种马的帧 URL(与 H5 PlayingView 一致,避免运行时拼路径)
const horseFrames: Record<number, string[]> = {}; const horseFrames: Record<number, string[]> = {};
...@@ -54,11 +51,13 @@ for (let h = 1; h <= 4; h++) { ...@@ -54,11 +51,13 @@ for (let h = 1; h <= 4; h++) {
interface Horse { interface Horse {
id: string; id: string;
playerId: string; // 对应玩家的唯一标识
horseType: number; // 1-4 颜色 horseType: number; // 1-4 颜色
lane: "top" | "bottom"; lane: "top" | "bottom";
frameIndex: number; // 当前帧 0-20 frameIndex: number; // 当前帧 0-20
frameTimer: number; // 帧切换计时器 frameTimer: number; // 帧切换计时器
startTime: number; // 开始移动的时间戳(ms)
durationMs: number; // 跑完全程时长(ms)
x: number; // 当前 X 位置
} }
const horses = ref<Horse[]>([]); const horses = ref<Horse[]>([]);
...@@ -112,10 +111,7 @@ watch( ...@@ -112,10 +111,7 @@ watch(
if (rankList && rankList.length > 0) { if (rankList && rankList.length > 0) {
console.log('[Rank1View] 排名列表更新 - 数据:', JSON.stringify(rankList.map(p => ({ nickname: p.nickname, score: p.score })))) console.log('[Rank1View] 排名列表更新 - 数据:', JSON.stringify(rankList.map(p => ({ nickname: p.nickname, score: p.score }))))
updateRankList(rankList); updateRankList(rankList);
// 游戏进行中时,同步马匹数量与玩家数量
if (isGameRunning.value) {
syncHorsesWithPlayers();
}
} }
}, },
{ immediate: true, deep: true }, { immediate: true, deep: true },
...@@ -141,7 +137,7 @@ const startIntro = async () => { ...@@ -141,7 +137,7 @@ const startIntro = async () => {
// Phase 2: 倒计时结束 → 游戏正式开始,背景动画+马匹启动 // Phase 2: 倒计时结束 → 游戏正式开始,背景动画+马匹启动
gameStarted.value = true; gameStarted.value = true;
isGameRunning.value = true; isGameRunning.value = true;
syncHorsesWithPlayers(); // 根据当前玩家数量初始化马匹 generateRandomHorses();
startHorseLoop(); startHorseLoop();
// 等待 60s 游戏倒计时完全结束 // 等待 60s 游戏倒计时完全结束
...@@ -161,12 +157,6 @@ let horseRafId: number | undefined; ...@@ -161,12 +157,6 @@ let horseRafId: number | undefined;
let horseLastTs = 0; let horseLastTs = 0;
const HORSE_FRAME_TICK_MS = 48; // 每帧间隔 ~20fps const HORSE_FRAME_TICK_MS = 48; // 每帧间隔 ~20fps
const onHorseAnimationEnd = (horseId: string) => {
const idx = horses.value.findIndex((h) => h.id === horseId);
if (idx >= 0) {
horses.value.splice(idx, 1);
}
};
const startHorseLoop = () => { const startHorseLoop = () => {
stopHorseLoop(); stopHorseLoop();
...@@ -178,12 +168,18 @@ const startHorseLoop = () => { ...@@ -178,12 +168,18 @@ const startHorseLoop = () => {
return; return;
} }
if (!horseLastTs) horseLastTs = ts; if (!horseLastTs) horseLastTs = ts;
const dt = Math.min(ts - horseLastTs, 50); const dt = Math.min(ts - horseLastTs, 200);
horseLastTs = ts; horseLastTs = ts;
for (const horse of horses.value) { for (const horse of horses.value) {
if (ts < horse.startTime) continue;
const elapsed = ts - horse.startTime;
const progress = Math.min(elapsed / horse.durationMs, 1);
horse.x = HORSE_START_X + (HORSE_END_X - HORSE_START_X) * progress;
horse.frameTimer += dt; horse.frameTimer += dt;
if (horse.frameTimer >= HORSE_FRAME_TICK_MS) { while (horse.frameTimer >= HORSE_FRAME_TICK_MS) {
horse.frameTimer -= HORSE_FRAME_TICK_MS; horse.frameTimer -= HORSE_FRAME_TICK_MS;
horse.frameIndex = (horse.frameIndex + 1) % HORSE_FRAME_COUNT; horse.frameIndex = (horse.frameIndex + 1) % HORSE_FRAME_COUNT;
} }
...@@ -201,65 +197,33 @@ const stopHorseLoop = () => { ...@@ -201,65 +197,33 @@ const stopHorseLoop = () => {
} }
}; };
// ========== 根据玩家数量生成马匹 ========== // ========== 自动随机生成 8-10 匹马 ==========
/** 获取有效玩家(排除虚位以待) */ let gameStartTime = 0;
const getValidPlayers = () => {
return props.rankList.filter(
(p) => p.nickname !== "虚位以待" && !p.wechat_id?.startsWith("empty-") && !p.userid?.startsWith("empty-"),
);
};
/** 创建一匹马 */ const generateRandomHorses = () => {
const createHorse = (playerId: string, lane: "top" | "bottom") => { const count = 8 + Math.floor(Math.random() * 3); // 8-10
const type = HORSE_TYPES[Math.floor(Math.random() * HORSE_TYPES.length)]!; const lanes: ("top" | "bottom")[] = ["top", "bottom"];
return { const newHorses: Horse[] = [];
const now = performance.now();
for (let i = 0; i < count; i++) {
const type = HORSE_TYPES[i % HORSE_TYPES.length]!;
const lane = lanes[i % 2]!;
const delayMs = i * 2500 + Math.random() * 1000; // 每匹马间隔2.5秒起步,加随机1秒
const durationMs = (40 + Math.random() * 40) * 1000; // 40-80秒,速度浮动更大
newHorses.push({
id: `horse-${++horseIdCounter}`, id: `horse-${++horseIdCounter}`,
playerId,
horseType: type, horseType: type,
lane, lane,
frameIndex: Math.floor(Math.random() * HORSE_FRAME_COUNT), frameIndex: Math.floor(Math.random() * HORSE_FRAME_COUNT),
frameTimer: 0, frameTimer: 0,
}; startTime: now + delayMs,
}; durationMs,
x: HORSE_START_X,
/** 根据玩家数量同步生成马匹 */
const syncHorsesWithPlayers = () => {
const validPlayers = getValidPlayers();
const currentHorseCount = horses.value.length;
if (validPlayers.length === 0) return;
if (currentHorseCount < validPlayers.length) {
// 需要增加马匹
const lanes: ("top" | "bottom")[] = ["top", "bottom"];
const needed = validPlayers.length - currentHorseCount;
const newHorses: Horse[] = [];
for (let i = 0; i < needed; i++) {
const playerIndex = currentHorseCount + i;
const player = validPlayers[playerIndex];
if (!player) break;
const playerId = player.userid || player.wechat_id || player.avatar || `player-${playerIndex}`;
const lane = lanes[newHorses.length % 2] ?? "top";
newHorses.push(createHorse(playerId, lane));
}
if (newHorses.length > 0) {
horses.value = [...horses.value, ...newHorses];
}
} else if (currentHorseCount > validPlayers.length) {
// 需要减少马匹(移除多出的)
// 保留对应玩家的马
const filtered = horses.value.filter((h) => {
const player = validPlayers.find(
(p) => (p.userid || p.wechat_id || p.avatar) === h.playerId,
);
return player !== undefined;
}); });
// 如果 filtered 数量仍多于玩家,取前 N 个
horses.value = filtered.slice(0, validPlayers.length);
} }
horses.value = newHorses;
}; };
// ========== 预加载所有马匹帧图片(防止切帧时闪烁/白屏)========== // ========== 预加载所有马匹帧图片(防止切帧时闪烁/白屏)==========
...@@ -277,7 +241,7 @@ onMounted(() => { ...@@ -277,7 +241,7 @@ onMounted(() => {
if ($is_run_local()) { if ($is_run_local()) {
gameStarted.value = true; gameStarted.value = true;
isGameRunning.value = true; isGameRunning.value = true;
syncHorsesWithPlayers(); // 本地调试模式下也同步马匹 generateRandomHorses();
startHorseLoop(); startHorseLoop();
} }
}); });
...@@ -364,9 +328,9 @@ const rankIcon = (index: number) => { ...@@ -364,9 +328,9 @@ const rankIcon = (index: number) => {
<div <div
v-for="horse in horses" v-for="horse in horses"
:key="horse.id" :key="horse.id"
class="horse-wrapper horse-running" class="horse-wrapper"
:class="`horse-lane-${horse.lane}`" :class="`horse-lane-${horse.lane}`"
@animationend="onHorseAnimationEnd(horse.id)" :style="{ transform: `translateX(${horse.x}px)` }"
> >
<div class="horse-sprite"> <div class="horse-sprite">
<img <img
...@@ -567,19 +531,6 @@ const rankIcon = (index: number) => { ...@@ -567,19 +531,6 @@ const rankIcon = (index: number) => {
bottom: 60px; bottom: 60px;
} }
/* 水平位移:60s 从左侧屏外跑到右边缘贴齐屏幕,避免提前跑出画面 */
.horse-running {
animation: horse-run v-bind(horseRunDuration) linear forwards;
}
@keyframes horse-run {
from {
transform: translateX(v-bind(horseStartX));
}
to {
transform: translateX(v-bind(horseEndX));
}
}
.horse-sprite { .horse-sprite {
position: relative; position: relative;
......
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