Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
fc-minigame
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈冲
fc-minigame
Commits
83aad560
Commit
83aad560
authored
Jun 12, 2026
by
董政锦
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'game3-PC马数据和位置问题、开始游戏时的闪灰底问题;' into 'main'
fix: game3 游戏页闪灰底问题; See merge request
!14
parents
3ad7c03b
c7cf6e57
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
124 additions
and
66 deletions
+124
-66
Game3.vue
frontend-h5/src/pages/game3/Game3.vue
+32
-35
utils.ts
frontend-large/src/commons/utils.ts
+28
-0
DesignStage.vue
frontend-large/src/components/DesignStage.vue
+2
-1
Game3.vue
frontend-large/src/pages/game3/Game3.vue
+6
-2
Rank1View.vue
frontend-large/src/pages/game3/views/Rank1View.vue
+56
-28
No files found.
frontend-h5/src/pages/game3/Game3.vue
View file @
83aad560
...
@@ -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
{
...
...
frontend-large/src/commons/utils.ts
View file @
83aad560
...
@@ -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
);
...
...
frontend-large/src/components/DesignStage.vue
View file @
83aad560
...
@@ -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
;
}
}
...
...
frontend-large/src/pages/game3/Game3.vue
View file @
83aad560
...
@@ -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
;
}
}
...
...
frontend-large/src/pages/game3/views/Rank1View.vue
View file @
83aad560
...
@@ -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
,
80
0
];
const
LANE_TOP_POSITIONS
=
[
310
,
510
,
72
0
];
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
);
}
}
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment