4 Commits

Author SHA1 Message Date
panFD
50f43c227a refactor(mission): 优化战斗关卡UI显示与逻辑
1.  调整MissionComp注释与战斗计时获取逻辑
2.  移除关卡波数显示中的总波数限制,只显示当前波数
3.  更新mission.prefab的UI布局与默认显示文本
4.  调整地图prefab的物体位置
5.  修改bg2动画的帧率与时长参数
2026-06-13 22:42:09 +08:00
panFD
9c2c0a1621 feat(map): add hero move animation component
新增了英雄移动组件,实现多个英雄的周期性往复移动和动画速度同步匹配
同时更新了预制件配置以适配新的组件和节点命名调整
2026-06-13 22:12:27 +08:00
panFD
b12270be66 fix(map): 强制开启信息弹窗的关闭按钮
移除技能卡预览模式下关闭按钮的条件显示逻辑,始终显示关闭节点
2026-06-13 17:57:10 +08:00
panFD
d926c08582 feat(ui): 为所有按钮添加全局点击音效
1. 重构按钮点击音效添加方式,通过重写Button原型的_onTouchEnded方法实现全局注入
2. 移除各业务组件中手动添加的按钮音效播放代码,统一音效播放逻辑
2026-06-13 16:56:41 +08:00
12 changed files with 229 additions and 51 deletions

View File

@@ -12074,7 +12074,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 459.147,
"x": 371.47,
"y": -29.439,
"z": 0
},
@@ -21924,7 +21924,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 459.147,
"x": 371.47,
"y": -29.439,
"z": 0
},

View File

@@ -7,11 +7,11 @@
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 5,
"sample": 1,
"speed": 1,
"wrapMode": 2,
"enableTrsBlending": false,
"_duration": 6,
"_duration": 30,
"_hash": 500763545,
"_tracks": [
{
@@ -77,7 +77,7 @@
"__type__": "cc.RealCurve",
"_times": [
0,
6
30
],
"_values": [
{
@@ -90,7 +90,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
},
{
"__type__": "cc.RealKeyframeValue",
@@ -102,7 +104,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
}
],
"preExtrapolation": 1,
@@ -118,7 +122,7 @@
"__type__": "cc.RealCurve",
"_times": [
0,
6
30
],
"_values": [
{
@@ -131,7 +135,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
},
{
"__type__": "cc.RealKeyframeValue",
@@ -143,7 +149,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
}
],
"preExtrapolation": 1,
@@ -159,7 +167,7 @@
"__type__": "cc.RealCurve",
"_times": [
0,
6
30
],
"_values": [
{
@@ -172,7 +180,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
},
{
"__type__": "cc.RealKeyframeValue",
@@ -184,7 +194,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
}
],
"preExtrapolation": 1,
@@ -251,7 +263,7 @@
"__type__": "cc.RealCurve",
"_times": [
0,
6
30
],
"_values": [
{
@@ -264,7 +276,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
},
{
"__type__": "cc.RealKeyframeValue",
@@ -276,7 +290,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
}
],
"preExtrapolation": 1,
@@ -292,7 +308,7 @@
"__type__": "cc.RealCurve",
"_times": [
0,
6
30
],
"_values": [
{
@@ -305,7 +321,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
},
{
"__type__": "cc.RealKeyframeValue",
@@ -317,7 +335,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
}
],
"preExtrapolation": 1,
@@ -333,7 +353,7 @@
"__type__": "cc.RealCurve",
"_times": [
0,
6
30
],
"_values": [
{
@@ -346,7 +366,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
},
{
"__type__": "cc.RealKeyframeValue",
@@ -358,7 +380,9 @@
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
"__editorExtras__": {
"broken": null
}
}
],
"preExtrapolation": 1,

View File

@@ -38,10 +38,13 @@
"_components": [
{
"__id__": 102
},
{
"__id__": 104
}
],
"_prefab": {
"__id__": 104
"__id__": 106
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -240,7 +243,7 @@
},
{
"__type__": "cc.Node",
"_name": "m7",
"_name": "h",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -618,7 +621,7 @@
},
{
"__type__": "cc.Node",
"_name": "a1",
"_name": "h",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -996,7 +999,7 @@
},
{
"__type__": "cc.Node",
"_name": "k1",
"_name": "h",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -1208,7 +1211,7 @@
},
{
"__type__": "cc.Node",
"_name": "hero2",
"_name": "hero4",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -1374,7 +1377,7 @@
},
{
"__type__": "cc.Node",
"_name": "m1",
"_name": "h",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -1586,7 +1589,7 @@
},
{
"__type__": "cc.Node",
"_name": "hero4",
"_name": "hero5",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -1752,7 +1755,7 @@
},
{
"__type__": "cc.Node",
"_name": "h1",
"_name": "h",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -1990,6 +1993,39 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "f2zvSV135PlYuUfadiV2hO"
},
{
"__type__": "46ae531x0VGib728jAKeNFe",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 105
},
"hero1": {
"__id__": 42
},
"hero2": {
"__id__": 62
},
"hero3": {
"__id__": 2
},
"hero4": {
"__id__": 62
},
"hero5": {
"__id__": 82
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "86bacQ+jZMOokmXQGUf5Ed"
},
{
"__type__": "cc.PrefabInfo",
"root": {

View File

@@ -180,7 +180,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"x": -259.716,
"y": 0,
"z": 0
},
@@ -271,7 +271,7 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 510,
"width": 300,
"height": 66
},
"_anchorPoint": {
@@ -313,7 +313,7 @@
},
"_type": 1,
"_fillType": 0,
"_sizeMode": 1,
"_sizeMode": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
@@ -443,7 +443,7 @@
"b": 255,
"a": 255
},
"_string": "第 1/20 波",
"_string": "第 1 波",
"_horizontalAlign": 1,
"_verticalAlign": 1,
"_actualFontSize": 26,
@@ -821,8 +821,8 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": -36.584,
"x": 295.012,
"y": 0,
"z": 0
},
"_lrot": {
@@ -1110,8 +1110,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 100,
"height": 100
"width": 720,
"height": 50
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1136,11 +1136,11 @@
"__prefab": {
"__id__": 44
},
"_alignFlags": 17,
"_alignFlags": 41,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 39.067999999999984,
"_top": 64.06799999999998,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
@@ -1150,7 +1150,7 @@
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 0,
"_originalWidth": 100,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
@@ -14302,7 +14302,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 761.798,
"y": 640,
"y": 1020,
"z": 0
},
"_lrot": {
@@ -14903,11 +14903,11 @@
"__prefab": {
"__id__": 701
},
"_alignFlags": 2,
"_alignFlags": 1,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 448.34799999999996,
"_top": 200,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
@@ -15377,7 +15377,7 @@
"propertyPath": [
"_active"
],
"value": true
"value": false
},
{
"__type__": "CCPropertyOverrideInfo",

View File

@@ -89,6 +89,7 @@ export class HeroViewComp extends CCComp {
private onHeroClicked() {
if (!this.model) return;
if (this.model.fac !== FacSet.HERO) return;
oops.audio.playEffect("music/button");
const eid = this.ent?.eid;
if (!eid) return;

View File

@@ -4,7 +4,7 @@
* @LastEditors: dgflash
* @LastEditTime: 2022-08-17 12:38:59
*/
import { Node } from "cc";
import { Node, Button } from "cc";
import { UICallbacks } from "../../../../extensions/oops-plugin-framework/assets/core/gui/layer/Defines";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { AsyncQueue, NextFunction } from "../../../../extensions/oops-plugin-framework/assets/libs/collection/AsyncQueue";
@@ -27,6 +27,19 @@ export class Initialize extends ecs.Entity {
debugMode: boolean = false; // 是否启用调试模式
protected init() {
var queue: AsyncQueue = new AsyncQueue();
// --- 全局注入:为所有 Button 组件增加点击音效 ---
const originalOnTouchEnded = Button.prototype["_onTouchEnded"];
if (originalOnTouchEnded) {
Button.prototype["_onTouchEnded"] = function(event) {
if (this.interactable && this.node && this.node.activeInHierarchy) {
oops.audio.playEffect("music/button");
}
originalOnTouchEnded.call(this, event);
};
}
// ---------------------------------------------
// 加载自定义资
this.loadCustom(queue);
// 加载多语言包

View File

@@ -275,8 +275,7 @@ export class HInfoComp extends CCComp {
this.previewPoolLv = poolLv;
this.isSkillCard = isSkillCard;
if (this.sell_node) this.sell_node.active = false;
// 如果是技能卡预览模式(即点击了战场上的技能),允许关闭弹窗
if (this.close_node) this.close_node.active = isSkillCard;
if (this.close_node) this.close_node.active = true;
this.cacheLabels();
this.refresh();
}

View File

@@ -0,0 +1,90 @@
import { _decorator, Component, Node, Animation, Vec3 } from "cc";
const { ccclass, property } = _decorator;
@ccclass('HeroMoveComp')
export class HeroMoveComp extends Component {
@property({ type: Node })
hero1: Node = null!;
@property({ type: Node })
hero2: Node = null!;
@property({ type: Node })
hero3: Node = null!;
@property({ type: Node })
hero4: Node = null!;
@property({ type: Node })
hero5: Node = null!;
// 控制移动速度的基础参数
private baseSpeed: number = 20; // 每秒向右移动的基础像素速度
private timer: number = 0;
// 为每个英雄保存其对应的 Animation 和随机参数
private heroDatas: { node: Node, anim: Animation | null, startX: number, phase: number }[] = [];
onLoad() {
const heroes = [this.hero1, this.hero2, this.hero3, this.hero4, this.hero5];
heroes.forEach(heroNode => {
if (heroNode) {
// 查找名为 "h" 的子节点
const hNode = heroNode.getChildByName("h");
let anim: Animation | null = null;
if (hNode) {
anim = hNode.getComponent(Animation);
// 确保有动画组件才尝试播放
if (anim && anim.clips && anim.clips.length > 0) {
anim.play("move");
}
}
this.heroDatas.push({
node: heroNode,
anim: anim,
startX: heroNode.position.x, // 记录初始X位置
phase: Math.random() * Math.PI * 2 // 随机初始相位
});
}
});
}
update(dt: number) {
this.timer += dt;
this.heroDatas.forEach(data => {
if (!data.node) return;
// 设置周期为 4 秒 (例如1秒加速向右1秒减速回原点1秒减速向左1秒加速回原点)
// 使用正弦波控制速度,余弦波控制位移(因为速度是位移的导数)
const cycleDuration = 4;
const frequency = (Math.PI * 2) / cycleDuration;
// 速度倍率,范围在 0.9 ~ 1.1 之间波动
// sin 波形在 4秒周期内正负各占 2秒
const speedMultiplier = 1.0 + Math.sin(this.timer * frequency + data.phase) * 0.1;
// 调整动画的播放速度
if (data.anim) {
// 检查状态是否存在,以避免没有播放动画时的错误
const state = data.anim.getState("move");
if (state && state.isPlaying) {
state.speed = speedMultiplier;
} else if (data.anim.clips && data.anim.clips.length > 0) {
// 如果停止了,尝试重新播放
data.anim.play("move");
}
}
// 根据时间计算位置偏移量
// 使用 -cos 函数使得当速度sin为最大时位置在原点
// 积分关系:速度 v = A * sin(wt),位移 s = - (A/w) * cos(wt)
// 这样能保证在一个周期内它始终围绕原点往复运动,最终回到原始点
const maxOffset = 30; // 左右偏离原点的最大像素距离
const currentOffset = -Math.cos(this.timer * frequency + data.phase) * maxOffset;
const pos = data.node.position;
// 相对于初始点进行位置设置,确保周期性回到原始点
data.node.setPosition(new Vec3(data.startX + currentOffset, pos.y, pos.z));
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "46ae5df5-c745-4689-bef6-f2300a78d15e",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -643,6 +643,7 @@ export class MissionCardComp extends CCComp {
}
/** 抽卡按钮释放 → 执行抽卡逻辑 */
private onDrawTouchEnd() {
oops.audio.playEffect("music/button");
this.playButtonClickAnim(this.cards_chou, () => this.onClickDraw());
}
/** 抽卡按钮取消 → 恢复缩放 */
@@ -655,6 +656,7 @@ export class MissionCardComp extends CCComp {
this.playButtonPressAnim(this.skill_refresh);
}
private onSkillDrawTouchEnd() {
oops.audio.playEffect("music/button");
this.playButtonClickAnim(this.skill_refresh, () => this.onClickSkillRefresh());
}
private onSkillDrawTouchCancel() {
@@ -665,6 +667,7 @@ export class MissionCardComp extends CCComp {
this.playButtonPressAnim(this.skill_ad_refresh);
}
private onSkillAdDrawTouchEnd() {
oops.audio.playEffect("music/button");
this.playButtonClickAnim(this.skill_ad_refresh, () => this.onClickSkillAdRefresh());
}
private onSkillAdDrawTouchCancel() {

View File

@@ -117,7 +117,7 @@ export class MissionComp extends CCComp {
// ======================== 运行时状态 ========================
/** 战斗已耗时(秒),正向计时 */
/** 本波战斗已耗时(秒),正向计时,用于自适应难度评估 */
clearTime: number = 0
/** 剩余复活次数 */
revive_times: number = 1;
@@ -242,7 +242,7 @@ export class MissionComp extends CCComp {
/** 更新时间/波数显示(仅在秒数变化时更新以减少 Label 操作) */
update_time() {
const remainSecond = Math.floor(this.clearTime);
const remainSecond = Math.floor(smc.vmdata.mission_data.fight_time);
if (remainSecond === this.lastTimeSecond) return;
this.lastTimeSecond = remainSecond;
let m = Math.floor(remainSecond / 60);
@@ -400,7 +400,7 @@ export class MissionComp extends CCComp {
const label = phaseNode.getComponent(Label);
if (label) {
const wave = Math.max(1, this.currentWave || (smc.vmdata && smc.vmdata.mission_data ? smc.vmdata.mission_data.level : 1) || 1);
label.string = `${wave}/15`;
label.string = `${wave}`;
}
// 阶段切换动感表现:只在进入战斗阶段跳动一下,让流程充满心流体验
@@ -640,6 +640,8 @@ export class MissionComp extends CCComp {
if (!smc.mission.play) return;
if (smc.mission.pause) return;
if (this.currentPhase !== MissionPhase.Prepare) return;
oops.audio.playEffect("music/button");
this.to_fight();
}

View File

@@ -117,6 +117,7 @@ export class SkillBoxComp extends CCComp {
private onNodeClicked() {
if (!this.initialized) return;
oops.audio.playEffect("music/button");
// 点击时弹出 HInfoComp传入卡牌 UUID 和等级以启用预览模式
const config = CardPoolList.find(c => c.uuid === this.s_uuid || c.skill === this.s_uuid);
const cardUuid = config ? config.uuid : this.s_uuid;