feat: 支持音频管线
This commit is contained in:
@@ -515,6 +515,10 @@
|
||||
"__uuid__": "cff2809d-6daa-4749-a911-bb99e97b4b54",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"buttonClickAudio": {
|
||||
"__uuid__": "798824f1-0e20-48b7-ad8a-fb24d55bf986",
|
||||
"__expectedType__": "cc.AudioClip"
|
||||
},
|
||||
"_id": "c2b3nbzv9JuZmP2jxQyN72"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { _decorator, Component, Prefab } from 'cc';
|
||||
import { _decorator, Component, Prefab, AudioClip } from 'cc';
|
||||
import { ViewManager } from './scripts/core/ViewManager';
|
||||
import { ToastManager } from './scripts/utils/ToastManager';
|
||||
import { AudioManager } from './scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,9 @@ export class main extends Component {
|
||||
@property({ type: Prefab, tooltip: 'Toast 预制体' })
|
||||
toastPrefab: Prefab | null = null;
|
||||
|
||||
@property({ type: AudioClip, tooltip: '通用按钮点击音效' })
|
||||
buttonClickAudio: AudioClip | null = null;
|
||||
|
||||
/**
|
||||
* onLoad 比 start 更早执行
|
||||
* 确保 ViewManager 在 PageLoading.start() 之前初始化
|
||||
@@ -109,5 +113,7 @@ export class main extends Component {
|
||||
if (this.toastPrefab) {
|
||||
ToastManager.instance.init(this.toastPrefab, this.node);
|
||||
}
|
||||
|
||||
AudioManager.instance.init(this.buttonClickAudio, this.node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { StaminaInfo } from 'db://assets/scripts/types/ApiTypes';
|
||||
import { AuthManager } from 'db://assets/scripts/utils/AuthManager';
|
||||
import { AchievementTitleManager } from 'db://assets/scripts/utils/AchievementTitleManager';
|
||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -103,6 +104,7 @@ export class PageHome extends BaseView {
|
||||
private _onStartGameClick(): void {
|
||||
if (this._isAnimating) return;
|
||||
|
||||
AudioManager.instance.playButtonClick();
|
||||
console.log('[PageHome] 开始游戏按钮点击');
|
||||
|
||||
// 体力检查
|
||||
@@ -138,6 +140,7 @@ export class PageHome extends BaseView {
|
||||
* PK按钮点击回调
|
||||
*/
|
||||
private _onPkClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
console.log('[PageHome] PK按钮点击');
|
||||
ViewManager.instance.open('PageWriteLevels');
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { CommonModal } from 'db://assets/prefabs/CommonModal';
|
||||
import { ApiEnvelope, StaminaInfo, NextLevelData, SubmitShareLevel } from 'db://assets/scripts/types/ApiTypes';
|
||||
import { AchievementTitleManager } from 'db://assets/scripts/utils/AchievementTitleManager';
|
||||
import { applyRoundedCorner } from 'db://assets/scripts/utils/roundedMaterial.utils';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -646,7 +647,11 @@ export class PageLevel extends BaseView {
|
||||
const editBox = inputNode.getComponent(EditBox);
|
||||
if (editBox) {
|
||||
editBox.placeholder = '';
|
||||
editBox.maxLength = chars.length;
|
||||
// 不限制单格 maxLength:iOS 拼音 / 日韩 IME 在选词前需要键入比目标字数更长的拼写串,
|
||||
// 例如答案"你好"(2 字)需键入"nihao"(5 字符)才能上屏;若 maxLength=2 会在第三个
|
||||
// 拼音字符就被原生键盘截断,用户连选词都做不到。最终长度限制由 distributeInputText
|
||||
// 在 EDITING_DID_ENDED 时裁剪到每格 1 字(见 applyInputTextToBlocks)。
|
||||
editBox.maxLength = -1;
|
||||
editBox.string = '';
|
||||
editBox.node.on(EditBox.EventType.EDITING_DID_BEGAN, this.onInputEditingBegan, this);
|
||||
editBox.node.on(EditBox.EventType.TEXT_CHANGED, this.onInputTextChanged, this);
|
||||
@@ -859,7 +864,7 @@ export class PageLevel extends BaseView {
|
||||
*/
|
||||
private onIconSettingClick(): void {
|
||||
console.log('[PageLevel] IconSetting 点击,返回主页');
|
||||
this.playClickSound();
|
||||
AudioManager.instance.playButtonClick();
|
||||
|
||||
// 分享模式下栈中没有 PageHome,需要清除分享状态并直接打开首页
|
||||
if (this._isShareMode) {
|
||||
@@ -2018,6 +2023,17 @@ export class PageLevel extends BaseView {
|
||||
onShare: () => {
|
||||
// 分享后不关闭弹窗,用户可继续点击下一关
|
||||
console.log('[PageLevel] 分享完成');
|
||||
},
|
||||
onHome: () => {
|
||||
this._closePassModal();
|
||||
|
||||
if (this._isShareMode) {
|
||||
ShareManager.instance.clearShareMode();
|
||||
ViewManager.instance.replace('PageHome');
|
||||
return;
|
||||
}
|
||||
|
||||
ViewManager.instance.back();
|
||||
}
|
||||
});
|
||||
// 动画消费完一次后清除起点,避免弹窗多次打开时复用
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { CreatedShareItem, ParticipatedShareItem, ShareParticipantRankSummary } from 'db://assets/scripts/types/ApiTypes';
|
||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('PagePKData')
|
||||
@@ -57,6 +58,7 @@ export class PagePKData extends BaseView {
|
||||
}
|
||||
|
||||
private _onBackClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
ViewManager.instance.back();
|
||||
}
|
||||
|
||||
@@ -189,14 +191,20 @@ export class PagePKData extends BaseView {
|
||||
|
||||
const viewButton = this._findChild(item, 'ViewButton');
|
||||
if (viewButton) {
|
||||
const handler = () => this._openShareDetail(share);
|
||||
const handler = () => {
|
||||
AudioManager.instance.playButtonClick();
|
||||
this._openShareDetail(share);
|
||||
};
|
||||
viewButton.on(Button.EventType.CLICK, handler, this);
|
||||
this._createdButtonBindings.push({ node: viewButton, handler });
|
||||
}
|
||||
|
||||
const shareButton = this._findChild(item, 'ShareButton');
|
||||
if (shareButton) {
|
||||
const handler = () => ShareManager.instance.triggerWxShare(share.title, share.shareCode);
|
||||
const handler = () => {
|
||||
AudioManager.instance.playButtonClick();
|
||||
ShareManager.instance.triggerWxShare(share.title, share.shareCode);
|
||||
};
|
||||
shareButton.on(Button.EventType.CLICK, handler, this);
|
||||
this._createdButtonBindings.push({ node: shareButton, handler });
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||
import { SubmitShareData, SubmittedShareLevelData } from 'db://assets/scripts/types/ApiTypes';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('PagePKEnd')
|
||||
@@ -105,6 +106,7 @@ export class PagePKEnd extends BaseView {
|
||||
}
|
||||
|
||||
private _onHomeClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
ShareManager.instance.clearShareMode();
|
||||
ViewManager.instance.replace('PageHome');
|
||||
}
|
||||
@@ -226,6 +228,7 @@ export class PagePKEnd extends BaseView {
|
||||
}
|
||||
|
||||
const handler = () => {
|
||||
AudioManager.instance.playButtonClick();
|
||||
if (answerButton?.isValid) {
|
||||
answerButton.active = false;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { _decorator, Node, Button, Label, ScrollView, instantiate, UITransform }
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { CompletedLevelsManager } from 'db://assets/scripts/utils/CompletedLevelsManager';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
import { PreviewLevelItem } from './PreviewLevelItem';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -215,6 +216,7 @@ export class PagePreviewLevels extends BaseView {
|
||||
// ─── 事件处理 ───────────────────────────────────────
|
||||
|
||||
private _onBackClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
console.log('[PagePreviewLevels] 返回');
|
||||
ViewManager.instance.back();
|
||||
}
|
||||
|
||||
@@ -5050,10 +5050,6 @@
|
||||
"__expectedType__": "cc.EffectAsset"
|
||||
},
|
||||
"coverCornerRadius": 0.1,
|
||||
"itemToggleAudio": {
|
||||
"__uuid__": "798824f1-0e20-48b7-ad8a-fb24d55bf986",
|
||||
"__expectedType__": "cc.AudioClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
@@ -5136,4 +5132,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { _decorator, Node, Button, Sprite, Label, Toggle, ScrollView, EditBox, instantiate, UITransform, Vec2, EventTouch, EffectAsset, AudioClip, AudioSource } from 'cc';
|
||||
import { _decorator, Node, Button, Sprite, Label, Toggle, ScrollView, EditBox, instantiate, UITransform, Vec2, EventTouch, EffectAsset } from 'cc';
|
||||
import { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||
import { CompletedLevelsManager } from 'db://assets/scripts/utils/CompletedLevelsManager';
|
||||
@@ -11,6 +11,7 @@ import { API_ENDPOINTS, API_TIMEOUT } from 'db://assets/scripts/config/ApiConfig
|
||||
import { HttpUtil } from 'db://assets/scripts/utils/HttpUtil';
|
||||
import { ApiEnvelope, CompletedLevel } from 'db://assets/scripts/types/ApiTypes';
|
||||
import { applyRoundedCorner } from 'db://assets/scripts/utils/roundedMaterial.utils';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -69,9 +70,6 @@ export class PageWriteLevels extends BaseView {
|
||||
@property({ tooltip: '关卡封面圆角半径比例(相对于短边,0-0.5)' })
|
||||
coverCornerRadius: number = 0.1;
|
||||
|
||||
@property({ type: AudioClip, tooltip: '关卡项选中/取消选中音效' })
|
||||
itemToggleAudio: AudioClip | null = null;
|
||||
|
||||
private _selectedIndices: Set<number> = new Set();
|
||||
private _levels: CompletedLevel[] = [];
|
||||
private _levelCount: number = 0;
|
||||
@@ -429,7 +427,7 @@ export class PageWriteLevels extends BaseView {
|
||||
this._selectedIndices.delete(index);
|
||||
}
|
||||
|
||||
this._playSound(this.itemToggleAudio);
|
||||
AudioManager.instance.playButtonClick();
|
||||
|
||||
console.log('[PageWriteLevels] item切换选中:', index, selected, '当前已选:', this._selectedIndices.size);
|
||||
|
||||
@@ -492,11 +490,13 @@ export class PageWriteLevels extends BaseView {
|
||||
}
|
||||
|
||||
private _onBackClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
console.log('[PageWriteLevels] 返回按钮点击');
|
||||
ViewManager.instance.back();
|
||||
}
|
||||
|
||||
private _onDataClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
ViewManager.instance.open('PagePKData');
|
||||
}
|
||||
|
||||
@@ -513,16 +513,8 @@ export class PageWriteLevels extends BaseView {
|
||||
return true;
|
||||
}
|
||||
|
||||
private _playSound(clip: AudioClip | null): void {
|
||||
if (!clip) {
|
||||
return;
|
||||
}
|
||||
|
||||
const audioSource = this.node.getComponent(AudioSource) ?? this.node.addComponent(AudioSource);
|
||||
audioSource?.playOneShot(clip);
|
||||
}
|
||||
|
||||
private _onPreviewClick(): void {
|
||||
AudioManager.instance.playButtonClick();
|
||||
if (!this._validateSelection()) return;
|
||||
const shareTitle = this.shareTitleEditBox?.getComponent(EditBox)?.string?.trim() || '';
|
||||
ViewManager.instance.open('PagePreviewLevels', {
|
||||
@@ -534,6 +526,7 @@ export class PageWriteLevels extends BaseView {
|
||||
}
|
||||
|
||||
private async _onCompleteClick(): Promise<void> {
|
||||
AudioManager.instance.playButtonClick();
|
||||
if (!this._validateSelection()) return;
|
||||
|
||||
const shareTitle = this.shareTitleEditBox?.getComponent(EditBox)?.string?.trim() || '';
|
||||
|
||||
@@ -4019,6 +4019,9 @@
|
||||
"nextLevelButton": {
|
||||
"__id__": 107
|
||||
},
|
||||
"settingButton": {
|
||||
"__id__": 154
|
||||
},
|
||||
"shareButton": {
|
||||
"__id__": 127
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { _decorator, Node, Label, AudioClip, AudioSource, view, UITransform, Size, ProgressBar, tween, Tween } from 'cc';
|
||||
import { BaseModal } from 'db://assets/scripts/core/BaseModal';
|
||||
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
||||
import { AudioManager } from 'db://assets/scripts/utils/AudioManager';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -11,6 +12,8 @@ export interface PassModalCallbacks {
|
||||
onNextLevel?: () => void;
|
||||
/** 点击分享回调 */
|
||||
onShare?: () => void;
|
||||
/** 点击返回主页回调 */
|
||||
onHome?: () => void;
|
||||
}
|
||||
|
||||
export interface PassModalTitleInfo {
|
||||
@@ -45,6 +48,10 @@ export class PassModal extends BaseModal {
|
||||
@property(Node)
|
||||
nextLevelButton: Node | null = null;
|
||||
|
||||
/** 返回主页按钮 */
|
||||
@property(Node)
|
||||
settingButton: Node | null = null;
|
||||
|
||||
/** 分享按钮 */
|
||||
@property(Node)
|
||||
shareButton: Node | null = null;
|
||||
@@ -142,6 +149,7 @@ export class PassModal extends BaseModal {
|
||||
*/
|
||||
onViewLoad(): void {
|
||||
console.log('[PassModal] onViewLoad');
|
||||
this._resolveNodes();
|
||||
this._resolveProgressAnchor();
|
||||
this._cacheProgressAnchorStartX();
|
||||
this._bindButtonEvents();
|
||||
@@ -198,6 +206,9 @@ export class PassModal extends BaseModal {
|
||||
if (this.nextLevelButton) {
|
||||
this.nextLevelButton.on(Node.EventType.TOUCH_END, this._onNextLevelClick, this);
|
||||
}
|
||||
if (this.settingButton) {
|
||||
this.settingButton.on(Node.EventType.TOUCH_END, this._onHomeClick, this);
|
||||
}
|
||||
if (this.shareButton) {
|
||||
this.shareButton.on(Node.EventType.TOUCH_END, this._onShareClick, this);
|
||||
}
|
||||
@@ -211,11 +222,20 @@ export class PassModal extends BaseModal {
|
||||
if (this.nextLevelButton && this.nextLevelButton.isValid) {
|
||||
this.nextLevelButton.off(Node.EventType.TOUCH_END, this._onNextLevelClick, this);
|
||||
}
|
||||
if (this.settingButton && this.settingButton.isValid) {
|
||||
this.settingButton.off(Node.EventType.TOUCH_END, this._onHomeClick, this);
|
||||
}
|
||||
if (this.shareButton && this.shareButton.isValid) {
|
||||
this.shareButton.off(Node.EventType.TOUCH_END, this._onShareClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
private _resolveNodes(): void {
|
||||
this.nextLevelButton = this.nextLevelButton ?? this.node.getChildByName('Button') ?? null;
|
||||
this.settingButton = this.settingButton ?? this.node.getChildByName('SettingButton') ?? null;
|
||||
this.shareButton = this.shareButton ?? this.node.getChildByName('Share') ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放通关音效
|
||||
*/
|
||||
@@ -481,4 +501,13 @@ export class PassModal extends BaseModal {
|
||||
|
||||
this._callbacks.onShare?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回主页按钮点击
|
||||
*/
|
||||
private _onHomeClick(): void {
|
||||
console.log('[PassModal] 点击返回主页');
|
||||
AudioManager.instance.playButtonClick();
|
||||
this._callbacks.onHome?.();
|
||||
}
|
||||
}
|
||||
|
||||
55
assets/scripts/utils/AudioManager.ts
Normal file
55
assets/scripts/utils/AudioManager.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { AudioClip, AudioSource, Node } from 'cc';
|
||||
|
||||
/**
|
||||
* 音效管理器
|
||||
* 统一管理全局按钮点击等通用音效,避免页面各自维护 AudioSource。
|
||||
*/
|
||||
export class AudioManager {
|
||||
private static _instance: AudioManager | null = null;
|
||||
|
||||
private _clickAudio: AudioClip | null = null;
|
||||
private _hostNode: Node | null = null;
|
||||
private _audioSource: AudioSource | null = null;
|
||||
|
||||
static get instance(): AudioManager {
|
||||
if (!this._instance) {
|
||||
this._instance = new AudioManager();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
init(clickAudio: AudioClip | null, hostNode: Node | null): void {
|
||||
this._clickAudio = clickAudio;
|
||||
this._hostNode = hostNode;
|
||||
this._audioSource = null;
|
||||
}
|
||||
|
||||
playButtonClick(): void {
|
||||
this._playOneShot(this._clickAudio);
|
||||
}
|
||||
|
||||
private _playOneShot(clip: AudioClip | null): void {
|
||||
if (!clip) {
|
||||
return;
|
||||
}
|
||||
|
||||
const audioSource = this._ensureAudioSource();
|
||||
audioSource?.playOneShot(clip);
|
||||
}
|
||||
|
||||
private _ensureAudioSource(): AudioSource | null {
|
||||
if (this._audioSource?.isValid) {
|
||||
return this._audioSource;
|
||||
}
|
||||
|
||||
if (!this._hostNode?.isValid) {
|
||||
console.warn('[AudioManager] 未初始化宿主节点,无法播放音效');
|
||||
return null;
|
||||
}
|
||||
|
||||
this._audioSource = this._hostNode.getComponent(AudioSource) ?? this._hostNode.addComponent(AudioSource);
|
||||
return this._audioSource;
|
||||
}
|
||||
}
|
||||
9
assets/scripts/utils/AudioManager.ts.meta
Normal file
9
assets/scripts/utils/AudioManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "2dc839e1-6bb2-4d35-a2c4-bf90f85f40e8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user