feat(games): 第一人稱視角切換(V/👁)——最小可玩版 - #21
Closed
toomore wants to merge 2 commits into
Closed
Conversation
主世界新增第一/第三人稱切換。之所以便宜,是因為既有架構剛好都站在對的那一邊: 移動早就是相機相對(camera.getWorldDirection 驅動 WASD,虛擬搖桿走同一條路徑), 互動全部是距離觸發(Math.hypot(hero.position…),唯一的 raycast 只打 terrain), 小地圖已經跟著 yaw 轉,碰撞是 XZ 剛體推出。所以這裡只換相機求值,不換操作系統。 (#11 曾移除過檔案室的第一人稱房間,原因是「室內 WASD 變平移、手機搖桿只能平移形同卡住」。 那是換一套操作;這次移動/互動/搖桿全部沿用主世界,只有相機不同。) 改了什麼 - 兩模式共用同一組 yaw/pitch:第一人稱視線 = 第三人稱「主角→相機」方位取負號。 方向語意一致(pitch 越大越往下看)→ 拖曳手感相同、切換時畫面不會上下顛倒。 可用範圍不同:第三人稱 [0.16, 1.2]、第一人稱 [-1.35, 1.0]。 - 視點:hero.position.y + 2.6(頭部中心),再往視線前方推 0.45。 不推的話相機正好架在自己軀幹(膠囊 r=0.55)上,稍微低頭就有一大塊橘色身體糊在畫面下緣。 0.45 是上限:HERO_R=0.6 讓身體停在牆前 0.6,相機推完離牆面還有 0.15 > near plane 0.1。 - 不 lerp、不夾地:相機長在身上,插值只會變拖曳感。走路的 0.12 踏步起伏直接變成免費的鏡頭晃動。 - 只藏頭(head.visible):髮與眼是 head 的子物件會一起消失。不整個 hero 隱藏—— three.js 中 visible=false 就不投影,白天主角影子會憑空不見。 - 身體恆跟著視線(hero.rotation.y = yaw + π):火把與地面光錐吃的是 hero.rotation.y, 若沿用「身體朝移動方向」,夜裡按 A 側移光束就會憑空甩到旁邊。 - FOV 52° → 68°:第三人稱的 52° 用在第一人稱像望遠鏡。 - 第一人稱關閉點地移動與滾輪縮放(dist 保留 → 切回去距離不變);主角對話泡隱藏 (掛在頭頂 y=3.6=相機正上方 1m,CSS2D 投影會跑掉)。 - 戰鬥分支補 head.visible = true:battle 自己接管相機且提早 return 跳過鏡頭段, 否則第一人稱切進戰鬥會是無頭主角。終局運鏡同理走第三人稱。 - UI:右上角 👁 按鈕(進行中反白)+ V 鍵;V 未與既有鍵位衝突。 驗證(headless Chrome + swiftshader,CDP 驅動真實 rAF,非虛擬時間) - 俯仰夾制走真實 pointer 事件:第一人稱夾在 -1.350 / 1.000,第三人稱仍是 0.160 / 1.2 - 切換往返:fov 68↔52、頭 false↔true、aria-pressed 同步,V 鍵與按鈕皆可 - 夜晚側移(A)時火把光錐仍鎖在視線正前方 → 身體跟視線的修正生效 - 白天村莊、高草區、極限俯仰(純天空/無破面)皆截圖確認 已知、刻意未處理 - 走到市集棚正前方時相機會鑽進遮陽帆布:帆布在 y=2.25–2.70、視點 2.6, 碰撞盒半深 1.0 讓身體停在 1.6、相機前推後在 1.15 < 帆布邊緣 1.35。 修法是把 addBoxCol 的半深加大,但那會連帶改變第三人稱「能走多靠近攤位」→ 留待決定。 - 最大俯角(1.0)畫面最下緣仍有一小條自己的軀幹。 - 切換為硬切,尚未做 dist 24→0 的補間。 - 按鈕 title 目前只有正體(與既有 shotbtn 一致),尚未進 i18n。 Signed-off-by: Toomore Chiang <toomore0929@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RzFUxR8R7kW8jLNAgWcTf7 Signed-off-by: Toomore Chiang <toomore0929@gmail.com>
回報:第一人稱的白天「超級亮」。量測後是 bloom 吃到未經 tone mapping 的天空輻射值。
成因
Sky addon 輸出的是物理輻射值(白天實測 12–25),它自己的 <tonemapping_fragment> 在離屏
render target 上是 no-op——three.js 對 render target 一律關 tone mapping,留給最後的 OutputPass。
於是整片天空以 12–25 的量進入 UnrealBloomPass(threshold 才 1.05),再被 radius 0.4 的大範圍糊開。
第三人稱俯視,畫面裡幾乎沒有原始天空,糊下來影響有限;第一人稱地平線落在畫面中央,
天空的 bloom 直接蓋住整個下半部。實測第一人稱地面過曝面積 33.6%、地面平均亮度 0.831。
作法
在天空自己的 shader 收斂:取最亮通道等比縮放(不偏色),knee=1.0 以下原封不動
(雲、日出日落層次全留著),之上漸近逼近 SKY_CEIL=1.8。
只動天空材質 → 水晶/火把/法師球等 emissive 的 bloom 結構上完全不經過這段。
(不走選擇性 bloom 雙 composer:已知會在部分裝置讓發光物消失,見 main.js 既有註解。)
量測(headless Chrome + swiftshader,天氣鎖定為晴、曝光 1.040、vibrancy 1.00 全程一致,
場景暖機 16s 等開場運鏡與群山沉降結束後才取樣,每組都做還原可逆驗證)
地面平均 地面過曝 天空平均 天空過曝
第一人稱 修正前 0.831 33.6% 0.957 92.5%
第一人稱 ceil3.2 0.721 3.4% 0.944 88.6%
第一人稱 ceil1.8 0.643 0.1% 0.915 68.6% ← 採用
第一人稱 還原 0.836 36.5% 0.956 92.0% ← 可逆,確認差異來自本改動
第三人稱 修正前 0.450 0.2% 0.731 24.9%
第三人稱 ceil1.8 0.416 0.1% 0.631 19.0%
第三人稱 還原 0.413 0.1% 0.616 18.8% ← 三者同在雜訊帶內=第三人稱不受影響
夜晚(vibrancy 0.00)發光物不受影響:
火把 亮部P99.5 0.9211 / 0.9211,>0.75 面積 2.13% / 2.14%(修正後 / 修正前)
水晶 亮部P99.5 0.9882 / 0.9863,>0.75 面積 63.95% / 63.34%
直視太陽(仰角 24°)也一併確認:修正前是一片純白虛空,修正後可見藍天漸層與地平線的樹。
量測方法上的一個坑(記下來免得再踩)
前三輪比較全部無效,因為沒有控制兩個變因:
1. 天氣系統會把 toneMappingExposure 壓低 45%(雷雨),隨機發生 → 同設定兩次量到 0.409 與 0.690。
2. 開場運鏡與群山沉降會讓畫面亮度在前 12 秒自己從 0.683 掉到 0.398,與本改動無關。
跑一組「完全不改參數、只推進時間」的對照組才發現。
所以最終量測都先暖機 16s、鎖定天氣,並在每組後把參數還原以確認差異可逆。
Signed-off-by: Toomore Chiang <toomore0929@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzFUxR8R7kW8jLNAgWcTf7
Signed-off-by: Toomore Chiang <toomore0929@gmail.com>
Contributor
Author
|
效果不如預期,放棄此方向。分支與 worktree 一併移除。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
主世界新增第一/第三人稱切換:右上角 👁 按鈕或 V 鍵。這是評估後的最小可玩版本,用來實地確認高草遮擋與世界尺度感,補間與 i18n 尚未做。
為什麼這麼便宜
既有架構剛好都站在對的那一邊,所以這裡只換相機求值,不換操作系統:
camera.getWorldDirection(fwd)驅動 WASD,kx += joyX讓虛擬搖桿走同一條路徑Math.hypot(hero.position…);唯一的 raycast 只打terrainmc.rotate(yaw)HERO_R = 0.6遠大於 near plane0.1改了什麼
兩模式共用同一組
yaw/pitch— 第一人稱視線=第三人稱「主角→相機」方位取負號。方向語意一致(pitch越大越往下看),所以拖曳手感相同、切換時畫面不會上下顛倒。差別只在範圍:第三人稱[0.16, 1.2]、第一人稱[-1.35, 1.0]。視點=
hero.position.y + 2.6(頭部中心),再往視線前方推0.45— 不推的話相機正好架在自己軀幹(膠囊r=0.55)上,稍微低頭就有一大塊橘色身體糊在畫面下緣。0.45是上限:HERO_R=0.6讓身體停在牆前 0.6,相機推完離牆面還有 0.15 > near plane 0.1,貼牆也不會穿進室內。不 lerp、不夾地 — 相機長在身上,插值只會變成拖曳感。走路時
hero.position.y本來就有 0.12 的踏步起伏,鏡頭晃動是免費附送的。只藏頭(
head.visible) — 髮與眼是head的子物件會一起消失。不整個hero隱藏:three.js 中visible=false就不投影,白天主角影子會憑空不見。身體恆跟著視線(
hero.rotation.y = yaw + π) — 火把與地面光錐吃的是hero.rotation.y。若沿用「身體朝移動方向」,夜裡按 A 側移光束就會憑空甩到旁邊。其餘 — FOV 52°→68°(52° 用在第一人稱像望遠鏡);第一人稱關閉點地移動與滾輪縮放(
dist保留,切回去距離不變);主角對話泡隱藏(掛在頭頂 y=3.6=相機正上方 1m,CSS2D 投影會跑掉);戰鬥分支補head.visible = true(battle 自己接管相機且提早return跳過鏡頭段,否則第一人稱切進戰鬥會是無頭主角)。驗證
headless Chrome + swiftshader,用 CDP 驅動真實 rAF(不是
--virtual-time-budget,那會讓 rAF 幾乎停擺):clampPitch):第一人稱夾在-1.350/1.000,第三人稱仍是0.160/1.2fov68↔52、頭false↔true、aria-pressed同步;V 鍵與按鈕皆可原本擔心的兩件事,實測結果:高草不是問題(視點高度看出去是漂亮的草浪,撥草半徑 1.7 有開出洞),尺度感也還好(FOV 調到 68 之後村莊比例正常)。
追加修正:白天過曝
回報「第一人稱的白天超級亮」。量測後不是霧、也不是 FOV,是 bloom 吃到了未經 tone mapping 的天空輻射值。
Sky addon 輸出物理輻射值(白天實測 12–25),它自己的
<tonemapping_fragment>在離屏 render target 上是 no-op——three.js 對 render target 一律關 tone mapping,留給最後的OutputPass。於是整片天空以 12–25 的量進入UnrealBloomPass(threshold 才 1.05),再被 radius 0.4 的大範圍糊開。第三人稱俯視、畫面裡幾乎沒有原始天空,糊下來影響有限;第一人稱地平線落在畫面中央,天空的 bloom 直接蓋住整個下半部。作法:在天空自己的 shader 收斂——取最亮通道等比縮放(不偏色),
SKY_KNEE=1.0以下原封不動(雲、日出日落層次全留著),之上漸近逼近SKY_CEIL=1.8。只動天空材質,emissive 的 bloom 結構上不經過這段。不走選擇性 bloom 雙 composer(main.js既有註解記載:已知會在部分裝置讓發光物消失)。第三人稱三列同在雜訊帶內 → 不受影響。每組都做還原可逆驗證,確認差異確實來自本改動。
夜晚發光物不受影響(vibrancy 0.00,修正後 / 修正前):火把亮部 P99.5
0.9211 / 0.9211、>0.75 面積2.13% / 2.14%;水晶0.9882 / 0.9863、63.95% / 63.34%。直視太陽(仰角 24°)修正前是一片純白虛空,修正後可見藍天漸層與地平線的樹。量測方法上的坑
前三輪比較全部無效,因為沒控制兩個變因:
toneMappingExposure壓低 45%(雷雨),隨機發生 → 同設定兩次量到 0.409 與 0.690。最終量測都先暖機 16s、鎖定天氣為晴,並在每組後還原參數確認可逆。
已知、刻意未處理
addBoxCol的半深加大,但那會連帶改變第三人稱「能走多靠近攤位」,屬於既有玩法的取捨 → 留待決定。dist24→0 的補間。shotbtn一致),尚未進 i18n。🤖 Generated with Claude Code
https://claude.ai/code/session_01RzFUxR8R7kW8jLNAgWcTf7