소스 검색

添加技能配置表和球控制器相关资源文件

181404010226 4 달 전
부모
커밋
df5b5e6362

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 193 - 148
assets/Scenes/GameLevel.scene


+ 1 - 4
assets/assets/Prefabs/Block001.prefab

@@ -428,10 +428,7 @@
     "__prefab": {
       "__id__": 19
     },
-    "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
-      "__expectedType__": "cc.Material"
-    },
+    "_customMaterial": null,
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

+ 1 - 4
assets/assets/Prefabs/Block002.prefab

@@ -428,10 +428,7 @@
     "__prefab": {
       "__id__": 19
     },
-    "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
-      "__expectedType__": "cc.Material"
-    },
+    "_customMaterial": null,
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

+ 1 - 4
assets/assets/Prefabs/Block003.prefab

@@ -519,10 +519,7 @@
     "__prefab": {
       "__id__": 23
     },
-    "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
-      "__expectedType__": "cc.Material"
-    },
+    "_customMaterial": null,
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

+ 1 - 4
assets/assets/Prefabs/Block004.prefab

@@ -610,10 +610,7 @@
     "__prefab": {
       "__id__": 27
     },
-    "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
-      "__expectedType__": "cc.Material"
-    },
+    "_customMaterial": null,
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

+ 1 - 4
assets/assets/Prefabs/Block005.prefab

@@ -610,10 +610,7 @@
     "__prefab": {
       "__id__": 27
     },
-    "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
-      "__expectedType__": "cc.Material"
-    },
+    "_customMaterial": null,
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

+ 21 - 0
assets/resources/data/excel/BallController配置表.csv

@@ -0,0 +1,21 @@
+球控制器参数,数据类型,默认值,有效范围,功能说明,备注
+baseSpeed,number,60,30-200,球的基础移动速度,影响球的移动快慢
+maxReflectionRandomness,number,0.2,0.0-1.0,反弹随机偏移最大角度,增加球反弹的随机性
+antiTrapTimeWindow,number,5.0,2.0-10.0,防围困检测时间窗口,检测球被围困的时间范围
+antiTrapHitThreshold,number,5,3-15,防围困撞击次数阈值,触发防围困机制的撞击次数
+deflectionAttemptThreshold,number,3,1-10,偏移尝试次数阈值,达到后使用穿透机制
+antiTrapDeflectionMultiplier,number,3.0,1.5-5.0,防围困偏移强度倍数,偏移力度的倍数
+FIRE_COOLDOWN,number,0.05,0.01-0.2,子弹发射冷却时间,控制子弹发射频率
+ballRadius,number,25,15-50,球的半径,影响碰撞检测范围
+restitution,number,1.0,0.8-1.2,弹性系数,控制碰撞后的反弹力度
+linearDamping,number,0,0-0.5,线性阻尼,控制球速度的衰减
+angularDamping,number,0,0-0.5,角阻尼,控制球旋转的衰减
+gravityScale,number,0,0-2.0,重力缩放,控制重力对球的影响
+friction,number,0,0-1.0,摩擦系数,控制球与表面的摩擦
+safeDistance,number,20,10-50,安全距离,球生成时与方块的最小距离
+edgeOffset,number,20,10-100,边缘偏移,球生成时距离边界的距离
+maxAttempts,number,50,20-100,最大尝试次数,寻找有效生成位置的最大尝试
+colliderGroup,number,1,1-10,碰撞组,球的碰撞组设置
+colliderTag,number,1,1-10,碰撞标签,球的碰撞标签设置
+checkInterval,number,0.1,0.05-0.5,检测间隔,防围困机制的检测间隔时间
+ballStarted,boolean,false,true/false,球是否已开始运动,控制球的运动状态

+ 11 - 0
assets/resources/data/excel/BallController配置表.csv.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "59701a24-120d-41da-9122-7b7b28564efd",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 141 - 0
assets/resources/data/excel/BallController配置表说明.md

@@ -0,0 +1,141 @@
+ BallController配置表说明文档
+
+## 概述
+本配置表用于配置游戏中小球控制器(BallController)的各项参数,策划可以通过调整这些参数来平衡游戏难度和体验。
+
+## 文件说明
+- **文件位置**: `assets/resources/data/excel/BallController配置表.csv`
+- **对应脚本**: `assets/scripts/CombatSystem/BallController.ts`
+- **格式说明**: 第一行为参数名称,第二行为中文翻译,第三行开始为具体配置数据
+
+## 参数详细说明
+
+### 核心运动参数
+
+#### baseSpeed (基础球速)
+- **默认值**: 60
+- **推荐范围**: 30-200
+- **说明**: 控制小球的基础移动速度,直接影响游戏节奏
+- **调整建议**: 
+  - 数值越大游戏越快,难度越高
+  - 新手关卡建议使用较低数值(30-50)
+  - 高难度关卡可使用较高数值(100-200)
+
+#### maxReflectionRandomness (反弹随机偏移角度)
+- **默认值**: 0.2
+- **推荐范围**: 0-1
+- **说明**: 球反弹时的随机偏移最大角度(弧度),增加游戏随机性
+- **调整建议**:
+  - 0表示完全规律反弹
+  - 0.2-0.5适合大多数情况
+  - 过高会导致球运动过于随机
+
+### 防围困机制参数
+
+#### antiTrapTimeWindow (防围困检测时间窗口)
+- **默认值**: 5.0秒
+- **推荐范围**: 3-10秒
+- **说明**: 检测球是否被困的时间窗口
+- **调整建议**: 时间越短,防围困机制越敏感
+
+#### antiTrapHitThreshold (防围困撞击阈值)
+- **默认值**: 5次
+- **推荐范围**: 3-10次
+- **说明**: 在时间窗口内连续撞击多少次后触发防围困
+- **调整建议**: 次数越少,防围困越容易触发
+
+#### deflectionAttemptThreshold (偏移尝试次数阈值)
+- **默认值**: 3次
+- **推荐范围**: 1-5次
+- **说明**: 偏移尝试次数达到后使用穿透而非偏移
+- **调整建议**: 影响防围困的处理方式
+
+#### antiTrapDeflectionMultiplier (防围困偏移强度倍数)
+- **默认值**: 3.0
+- **推荐范围**: 1.5-5.0
+- **说明**: 防围困偏移的强度倍数
+- **调整建议**: 数值越大,偏移效果越明显
+
+### 战斗系统参数
+
+#### FIRE_COOLDOWN (子弹发射冷却时间)
+- **默认值**: 0.05秒
+- **推荐范围**: 0.01-0.2秒
+- **说明**: 方块发射子弹的间隔时间
+- **调整建议**: 
+  - 数值越小,子弹发射越频繁
+  - 影响游戏的战斗节奏
+
+### 物理系统参数
+
+#### ballRadius (球半径)
+- **默认值**: 25
+- **推荐范围**: 15-40
+- **说明**: 小球的碰撞半径
+- **调整建议**: 影响碰撞检测的精度和游戏难度
+
+#### restitution (弹性系数)
+- **默认值**: 1
+- **推荐范围**: 0.8-1.0
+- **说明**: 碰撞弹性系数,1为完全弹性碰撞
+- **调整建议**: 数值越小,球的弹跳越弱
+
+#### linearDamping (线性阻尼)
+- **默认值**: 0
+- **推荐范围**: 0-0.1
+- **说明**: 刚体线性阻尼,0表示无阻尼
+- **调整建议**: 通常保持为0以保持球的恒定速度
+
+### 生成系统参数
+
+#### safeDistance (安全距离)
+- **默认值**: 20
+- **推荐范围**: 10-50
+- **说明**: 小球生成时与方块的最小距离
+- **调整建议**: 避免球生成时立即碰撞
+
+#### edgeOffset (边缘偏移)
+- **默认值**: 20
+- **推荐范围**: 10-30
+- **说明**: 距离游戏区域边缘的偏移量
+- **调整建议**: 避免球生成在边缘位置
+
+#### maxAttempts (最大尝试次数)
+- **默认值**: 50
+- **推荐范围**: 20-100
+- **说明**: 寻找有效生成位置的最大尝试次数
+- **调整建议**: 影响生成算法的性能
+
+### 系统参数
+
+#### checkInterval (检查间隔)
+- **默认值**: 1.0秒
+- **推荐范围**: 0.5-2.0秒
+- **说明**: 状态检查的时间间隔
+- **调整建议**: 影响系统检查的频率
+
+## 使用建议
+
+### 难度调整
+1. **简单难度**: baseSpeed=40, maxReflectionRandomness=0.1
+2. **普通难度**: baseSpeed=60, maxReflectionRandomness=0.2
+3. **困难难度**: baseSpeed=100, maxReflectionRandomness=0.3
+4. **地狱难度**: baseSpeed=150, maxReflectionRandomness=0.4
+
+### 关卡设计
+- 早期关卡建议使用较低的球速和较强的防围困机制
+- 后期关卡可以适当提高球速,减弱防围困机制
+- 特殊关卡可以调整物理参数创造独特体验
+
+## 注意事项
+
+1. **性能考虑**: 过低的冷却时间可能影响性能
+2. **平衡性**: 极端数值可能破坏游戏平衡
+3. **测试建议**: 每次调整后都应进行充分测试
+4. **备份**: 修改前请备份原始配置
+
+## 更新日志
+
+- 2024-01-XX: 初始版本创建
+- 包含20个核心配置参数
+- 支持完整的球控制器功能配置

+ 11 - 0
assets/resources/data/excel/BallController配置表说明.md.meta

@@ -0,0 +1,11 @@
+{
+  "ver": "1.0.1",
+  "importer": "text",
+  "imported": true,
+  "uuid": "c77fffcf-7e29-4e38-b678-0f102503d752",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

BIN
assets/resources/data/excel/技能配置表.xlsx


+ 12 - 0
assets/resources/data/excel/技能配置表.xlsx.meta

@@ -0,0 +1,12 @@
+{
+  "ver": "1.0.0",
+  "importer": "*",
+  "imported": true,
+  "uuid": "08ed19d7-b25d-44ae-81ce-c7dcfa5f099d",
+  "files": [
+    ".json",
+    ".xlsx"
+  ],
+  "subMetas": {},
+  "userData": {}
+}

+ 0 - 0
assets/resources/shaders/ui-sprite-gray-material.mtl → assets/resources/shaders/ui-sprite-white-material.mtl


+ 0 - 0
assets/resources/shaders/ui-sprite-gray-material.mtl.meta → assets/resources/shaders/ui-sprite-white-material.mtl.meta


+ 51 - 9
assets/scripts/Animations/BallAni.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, Vec3, tween, Color,find, Sprite, resources, sp } from 'cc';
+import { _decorator, Component, Node, Vec3, tween, Color,find, Sprite, resources, sp, Material } from 'cc';
 import EventBus, { GameEvents } from '../Core/EventBus';
 const { ccclass, property } = _decorator;
 
@@ -52,19 +52,61 @@ export class BallAni extends Component {
         });
         
         console.log('[BallAni] 开始播放方块撞击动画', blockNode.name, '有敌人:', hasActiveEnemies);
+        console.log('[BallAni] hasActiveEnemies 检查结果:', hasActiveEnemies);
         
         const sprite = blockNode.getComponent(Sprite);
-        const originalScale = blockNode.scale.clone();
         
-        console.log('[BallAni] 原始缩放:', originalScale);
+        // 保存原始材质和Sprite缩放
+        const originalMaterial = sprite ? sprite.material : null;
+        const originalSpriteScale = sprite ? new Vec3(1, 1, 1) : new Vec3(1, 1, 1);
+        
+        console.log('[BallAni] 开始方块视觉缩放动画');
         
         if (hasActiveEnemies) {
-            // 有敌人时播放缩放动画
-            const shrinkScale = originalScale.clone().multiplyScalar(0.5);
-            const animationTween = tween(blockNode)
-                .to(0.2, { scale: shrinkScale })
-                .to(0.2, { scale: originalScale })
+            // 有敌人时播放Sprite缩放动画(不影响碰撞体积)
+            const shrinkSpriteScale = new Vec3(0.5, 0.5, 1);
+            
+            // 应用材质
+            console.log('[BallAni] 检查材质状态:', {
+                hasSprite: !!sprite,
+                hasCustomMaterial: sprite ? !!sprite.customMaterial : false,
+                currentMaterial: sprite ? sprite.material : null
+            });
+            
+            if (sprite && sprite.customMaterial) {
+                sprite.material = sprite.customMaterial;
+                console.log('[BallAni] 应用自定义材质成功');
+            } else if (sprite) {
+                // 动态加载灰色材质
+                const grayMaterialPath = 'shaders/ui-sprite-white-material.mtl';
+                resources.load(grayMaterialPath, Material, (err, material) => {
+                    if (!err && material && sprite && sprite.isValid) {
+                        sprite.material = material;
+                        console.log('[BallAni] 动态加载并应用灰色材质成功');
+                    } else {
+                        console.log('[BallAni] 无法加载灰色材质,不使用任何材质');
+                    }
+                });
+            }
+            
+            // 对Sprite节点进行缩放动画(不影响方块碰撞体积)
+            const animationTween = tween(sprite.node)
+                .to(0.2, { scale: shrinkSpriteScale })
+                .to(0.2, { scale: originalSpriteScale })
                 .call(() => {
+                    // 动画完成时恢复原始材质
+                    console.log('[BallAni] 恢复材质状态:', {
+                        hasSprite: !!sprite,
+                        hasOriginalMaterial: !!originalMaterial,
+                        currentMaterial: sprite ? sprite.material : null
+                    });
+                    
+                    if (sprite && originalMaterial) {
+                        sprite.material = originalMaterial;
+                        console.log('[BallAni] 恢复原始材质成功');
+                    } else {
+                        console.log('[BallAni] 无法恢复原始材质 - 原始材质丢失');
+                    }
                     console.log('[BallAni] 方块动画完成,恢复原状');
                     // 动画完成,从活动列表中移除
                     this.activeBlockAnimations.delete(blockNode);
@@ -74,7 +116,7 @@ export class BallAni extends Component {
             // 添加到活动动画列表
             this.activeBlockAnimations.set(blockNode, animationTween);
         } else {
-            // 没有敌人时直接结束动画
+            // 没有敌人时直接结束动画,不应用材质
             const animationTween = tween({})
                 .delay(0.2)
                 .call(() => {

+ 0 - 198
assets/scripts/Animations/ButtonAni.ts

@@ -1,198 +0,0 @@
-import { _decorator, Component, Node, Button, Vec3, tween, find } from 'cc';
-const { ccclass, property } = _decorator;
-
-/**
- * ButtonAni - 通用按钮按压效果管理器
- * 自动为场景中所有按钮添加按压缩放效果
- * 挂载在场景的根节点或UI管理节点上
- */
-@ccclass('ButtonAni')
-export class ButtonAni extends Component {
-    
-    @property({ tooltip: '按压时的缩放比例' })
-    public pressScale: number = 0.9;
-    
-    @property({ tooltip: '按压动画时长(秒)' })
-    public pressDuration: number = 0.1;
-    
-    @property({ tooltip: '释放动画时长(秒)' })
-    public releaseDuration: number = 0.1;
-    
-    @property({ tooltip: '是否自动查找所有按钮' })
-    public autoFindButtons: boolean = true;
-    
-    @property({ type: [Node], tooltip: '手动指定的按钮节点列表(如果不自动查找)' })
-    public manualButtons: Node[] = [];
-    
-    // 存储按钮的原始缩放值
-    private originalScales: Map<Node, Vec3> = new Map();
-    
-    // 存储正在播放动画的按钮
-    private animatingButtons: Set<Node> = new Set();
-    
-    start() {
-        this.initializeButtonEffects();
-    }
-    
-    /**
-     * 初始化所有按钮的按压效果
-     */
-    private initializeButtonEffects() {
-        const buttons = this.getAllButtons();
-        console.log(`[ButtonAni] 找到 ${buttons.length} 个按钮,开始添加按压效果`);
-        
-        buttons.forEach(buttonNode => {
-            this.addButtonPressEffect(buttonNode);
-        });
-    }
-    
-    /**
-     * 获取所有需要添加效果的按钮
-     */
-    private getAllButtons(): Node[] {
-        if (!this.autoFindButtons) {
-            return this.manualButtons.filter(btn => btn && btn.isValid);
-        }
-        
-        const allButtons: Node[] = [];
-        
-        // 从Canvas开始递归查找所有按钮
-        const canvas = find('Canvas');
-        if (canvas) {
-            this.findButtonsRecursively(canvas, allButtons);
-        }
-        
-        // 也查找Canvas-001(如果存在)
-        const canvas001 = find('Canvas-001');
-        if (canvas001) {
-            this.findButtonsRecursively(canvas001, allButtons);
-        }
-        
-        return allButtons;
-    }
-    
-    /**
-     * 递归查找节点下的所有按钮组件
-     */
-    private findButtonsRecursively(node: Node, buttons: Node[]) {
-        if (!node || !node.isValid) return;
-        
-        // 检查当前节点是否有Button组件
-        const button = node.getComponent(Button);
-        if (button && button.enabled) {
-            buttons.push(node);
-        }
-        
-        // 递归检查子节点
-        node.children.forEach(child => {
-            this.findButtonsRecursively(child, buttons);
-        });
-    }
-    
-    /**
-     * 为单个按钮添加按压效果
-     */
-    private addButtonPressEffect(buttonNode: Node) {
-        if (!buttonNode || !buttonNode.isValid) return;
-        
-        const button = buttonNode.getComponent(Button);
-        if (!button) return;
-        
-        // 保存原始缩放值
-        this.originalScales.set(buttonNode, buttonNode.scale.clone());
-        
-        // 添加按下事件监听
-        buttonNode.on(Node.EventType.TOUCH_START, this.onButtonPress.bind(this, buttonNode), this);
-        buttonNode.on(Node.EventType.TOUCH_END, this.onButtonRelease.bind(this, buttonNode), this);
-        buttonNode.on(Node.EventType.TOUCH_CANCEL, this.onButtonRelease.bind(this, buttonNode), this);
-        
-        console.log(`[ButtonAni] 已为按钮 ${buttonNode.name} 添加按压效果`);
-    }
-    
-    /**
-     * 按钮按下事件处理
-     */
-    private onButtonPress(buttonNode: Node) {
-        if (!buttonNode || !buttonNode.isValid || this.animatingButtons.has(buttonNode)) {
-            return;
-        }
-        
-        this.animatingButtons.add(buttonNode);
-        
-        const originalScale = this.originalScales.get(buttonNode) || Vec3.ONE;
-        const pressedScale = originalScale.clone().multiplyScalar(this.pressScale);
-        
-        // 停止之前的动画
-        tween(buttonNode).stop();
-        
-        // 播放按压动画
-        tween(buttonNode)
-            .to(this.pressDuration, { scale: pressedScale }, { easing: 'quadOut' })
-            .call(() => {
-                // 动画完成后从集合中移除
-                this.animatingButtons.delete(buttonNode);
-            })
-            .start();
-    }
-    
-    /**
-     * 按钮释放事件处理
-     */
-    private onButtonRelease(buttonNode: Node) {
-        if (!buttonNode || !buttonNode.isValid) {
-            return;
-        }
-        
-        this.animatingButtons.add(buttonNode);
-        
-        const originalScale = this.originalScales.get(buttonNode) || Vec3.ONE;
-        
-        // 停止之前的动画
-        tween(buttonNode).stop();
-        
-        // 播放释放动画
-        tween(buttonNode)
-            .to(this.releaseDuration, { scale: originalScale }, { easing: 'backOut' })
-            .call(() => {
-                // 动画完成后从集合中移除
-                this.animatingButtons.delete(buttonNode);
-            })
-            .start();
-    }
-    
-    /**
-     * 手动刷新按钮效果(当有新按钮动态创建时调用)
-     */
-    public refreshButtonEffects() {
-        // 清理之前的监听
-        this.cleanup();
-        
-        // 重新初始化
-        this.initializeButtonEffects();
-    }
-    
-    /**
-     * 清理所有按钮监听和动画
-     */
-    private cleanup() {
-        this.originalScales.forEach((scale, buttonNode) => {
-            if (buttonNode && buttonNode.isValid) {
-                // 移除事件监听
-                buttonNode.off(Node.EventType.TOUCH_START, this.onButtonPress, this);
-                buttonNode.off(Node.EventType.TOUCH_END, this.onButtonRelease, this);
-                buttonNode.off(Node.EventType.TOUCH_CANCEL, this.onButtonRelease, this);
-                
-                // 停止动画并恢复原始缩放
-                tween(buttonNode).stop();
-                buttonNode.setScale(scale);
-            }
-        });
-        
-        this.originalScales.clear();
-        this.animatingButtons.clear();
-    }
-    
-    onDestroy() {
-        this.cleanup();
-    }
-}

+ 0 - 9
assets/scripts/Animations/ButtonAni.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "28dc4a8d-602e-4955-8867-7d713b5b2ef9",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 1 - 1
assets/scripts/CombatSystem/BallController.ts

@@ -24,7 +24,7 @@ export class BallController extends Component {
 
     // 球的移动速度
     @property
-    public baseSpeed: number = 60; // 原speed改为baseSpeed
+    public baseSpeed: number = 60; 
     public currentSpeed: number = 60;
     
     // 反弹随机偏移最大角度(弧度)

+ 138 - 11
assets/scripts/CombatSystem/BlockManager.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, Rect, Label, Color, Size, Sprite, SpriteFrame, resources, Button, Collider2D } from 'cc';
+import { _decorator, Component, Node, Prefab, instantiate, Vec3, EventTouch, Vec2, UITransform, find, Rect, Label, Color, Size, Sprite, SpriteFrame, resources, Button, Collider2D, Material } from 'cc';
 import { ConfigManager, WeaponConfig } from '../Core/ConfigManager';
 import { SaveDataManager } from '../LevelSystem/SaveDataManager';
 import { LevelSessionManager } from '../Core/LevelSessionManager';
@@ -1365,6 +1365,8 @@ export class BlockManager extends Component {
         console.log(`[BlockManager] 设置方块稀有度颜色: ${rarity}`, color);
     }
     
+
+    
     // 加载武器图标
     private loadWeaponIcon(block: Node, weaponConfig: WeaponConfig) {
         // 根据预制体结构:WeaponBlock -> B1 -> Weapon
@@ -1426,12 +1428,88 @@ export class BlockManager extends Component {
             spriteFrame && spriteFrame.isValid) {
             weaponSprite.spriteFrame = spriteFrame;
             
-            // 根据武器类型和方块形状旋转图片
+            // 应用武器图标的位置偏移和旋转
             this.rotateWeaponIconByShape(weaponNode, blockShapeId, weaponConfig.id);
         }
         });
     }
     
+    // 武器类型和方块形状组合的位置偏移配置(相对于方块中心的偏移量)
+    private readonly WEAPON_SHAPE_POSITION_OFFSETS: { [weaponId: string]: { [shapeId: string]: { x: number, y: number } } } = {
+        'pea_shooter': {
+            'I': { x: 3, y: -3 },      // 毛豆射手竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 5 },     // 毛豆射手横条形状,居中
+            'L': { x: 1, y: -10 },     // 毛豆射手L型,轻微向左下偏移
+            'S': { x: 5, y: -1 },      // 毛豆射手S型,轻微向右下偏移
+            'D-T': { x: 3, y: -1 }     // 毛豆射手倒T型,轻微向下偏移
+        },
+        'sharp_carrot': {
+            'I': { x: 0, y: -1 },      // 尖胡萝卜竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 尖胡萝卜横条形状,居中
+            'L': { x: -1, y: -5 },     // 尖胡萝卜L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 尖胡萝卜S型,轻微向右下偏移
+            'D-T': { x: 0, y: -1 }     // 尖胡萝卜倒T型,轻微向下偏移
+        },
+        'saw_grass': {
+            'I': { x: 0, y: -1 },      // 锯齿草竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 锯齿草横条形状,居中
+            'L': { x: -1, y: -1 },     // 锯齿草L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 锯齿草S型,轻微向右下偏移
+            'D-T': { x: 0, y: -5 }     // 锯齿草倒T型,轻微向下偏移
+        },
+        'watermelon_bomb': {
+            'I': { x: 0, y: -1 },      // 西瓜炸弹竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 西瓜炸弹横条形状,居中
+            'L': { x: -1, y: -1 },     // 西瓜炸弹L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 西瓜炸弹S型,轻微向右下偏移
+            'D-T': { x: 0, y: -7 }     // 西瓜炸弹倒T型,轻微向下偏移
+        },
+        'boomerang_plant': {
+            'I': { x: 0, y: -1 },      // 回旋镖植物竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 回旋镖植物横条形状,居中
+            'L': { x: -1, y: -8 },     // 回旋镖植物L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 回旋镖植物S型,轻微向右下偏移
+            'D-T': { x: -10, y: -13 }     // 回旋镖植物倒T型,轻微向下偏移
+        },
+        'hot_pepper': {
+            'I': { x: 0, y: -1 },      // 辣椒竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 辣椒横条形状,居中
+            'L': { x: -1, y: -1 },     // 辣椒L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 辣椒S型,轻微向右下偏移
+            'D-T': { x: 0, y: -1 }     // 辣椒倒T型,轻微向下偏移
+        },
+        'cactus_shotgun': {
+            'I': { x: 0, y: -1 },      // 仙人掌霰弹枪竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 仙人掌霰弹枪横条形状,居中
+            'L': { x: -1, y: -1 },     // 仙人掌霰弹枪L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 仙人掌霰弹枪S型,轻微向右下偏移
+            'D-T': { x: 0, y: -1 }     // 仙人掌霰弹枪倒T型,轻微向下偏移
+        },
+        'okra_missile': {
+            'I': { x: 0, y: -1 },      // 秋葵导弹竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 秋葵导弹横条形状,居中
+            'L': { x: -1, y: -1 },     // 秋葵导弹L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 秋葵导弹S型,轻微向右下偏移
+            'D-T': { x: 0, y: -1 }     // 秋葵导弹倒T型,轻微向下偏移
+        },
+        'mace_club': {
+            'I': { x: 0, y: -1 },      // 狼牙棒竖条形状,轻微向下偏移
+            'H-I': { x: 0, y: 0 },     // 狼牙棒横条形状,居中
+            'L': { x: -1, y: -1 },     // 狼牙棒L型,轻微向左下偏移
+            'S': { x: 1, y: -1 },      // 狼牙棒S型,轻微向右下偏移
+            'D-T': { x: 0, y: -1 }     // 狼牙棒倒T型,轻微向下偏移
+        }
+    };
+    
+    // 默认位置偏移配置(当武器类型未配置时使用)
+    private readonly DEFAULT_SHAPE_POSITION_OFFSETS: { [key: string]: { x: number, y: number } } = {
+        'I': { x: 0, y: -1 },      // 竖条形状,轻微向下偏移
+        'H-I': { x: 0, y: 0 },     // 横I型,居中
+        'L': { x: -1, y: -1 },     // L型,轻微向左下偏移
+        'S': { x: 1, y: -1 },      // S型,轻微向右下偏移
+        'D-T': { x: 0, y: -1 }     // 倒T型,轻微向下偏移
+    };
+    
     // 武器类型和方块形状组合的旋转角度配置
     private readonly WEAPON_SHAPE_ROTATION_ANGLES: { [weaponId: string]: { [shapeId: string]: number } } = {
         'pea_shooter': {
@@ -1443,7 +1521,7 @@ export class BlockManager extends Component {
         },
         'sharp_carrot': {
             'I': 0,      // 尖胡萝卜竖条形状
-            'H-I': 90,   // 尖胡萝卜横条形状,旋转适配水平方向
+            'H-I': 0,   // 尖胡萝卜横条形状,旋转适配水平方向
             'L': -15,    // 尖胡萝卜L型,轻微调整
             'S': 15,     // 尖胡萝卜S型,轻微调整
             'D-T': 0     // 尖胡萝卜倒T型
@@ -1466,8 +1544,8 @@ export class BlockManager extends Component {
             'I': 0,      // 回旋镖植物竖条形状
             'H-I': 90,   // 回旋镖植物横条形状,旋转适配飞行方向
             'L': -30,    // 回旋镖植物L型
-            'S': 45,     // 回旋镖植物S型,适配回旋轨迹
-            'D-T': 0     // 回旋镖植物倒T型
+            'S': -10,     // 回旋镖植物S型,适配回旋轨迹
+            'D-T': -80     // 回旋镖植物倒T型
         },
         'hot_pepper': {
             'I': 0,      // 辣椒竖条形状
@@ -1508,24 +1586,31 @@ export class BlockManager extends Component {
         'D-T': 0     // 倒T型,保持原始方向
     };
     
-    // 根据武器类型和方块形状旋转武器图标
+    // 根据武器类型和方块形状调整武器图标位置和旋转
     private rotateWeaponIconByShape(weaponNode: Node, shapeId: string | null, weaponId?: string) {
         if (!weaponNode || !shapeId) return;
         
         let rotationAngle = 0;
+        let positionOffset = { x: 0, y: 0 };
         
-        // 如果提供了武器ID,尝试获取特定武器的旋转角度
+        // 如果提供了武器ID,尝试获取特定武器的配置
         if (weaponId && this.WEAPON_SHAPE_ROTATION_ANGLES[weaponId]) {
             rotationAngle = this.WEAPON_SHAPE_ROTATION_ANGLES[weaponId][shapeId] || 0;
-            console.log(`为武器 ${weaponId} 形状 ${shapeId} 设置特定旋转角度: ${rotationAngle}度`);
+            positionOffset = this.WEAPON_SHAPE_POSITION_OFFSETS[weaponId]?.[shapeId] || { x: 0, y: 0 };
+            console.log(`为武器 ${weaponId} 形状 ${shapeId} 设置特定配置: 旋转${rotationAngle}度, 偏移(${positionOffset.x}, ${positionOffset.y})`);
         } else {
-            // 使用默认旋转角度配置
+            // 使用默认配置
             rotationAngle = this.DEFAULT_SHAPE_ROTATION_ANGLES[shapeId] || 0;
-            console.log(`为形状 ${shapeId} 设置默认旋转角度: ${rotationAngle}度`);
+            positionOffset = this.DEFAULT_SHAPE_POSITION_OFFSETS[shapeId] || { x: 0, y: 0 };
+            console.log(`为形状 ${shapeId} 设置默认配置: 旋转${rotationAngle}度, 偏移(${positionOffset.x}, ${positionOffset.y})`);
         }
         
         // 应用旋转
         weaponNode.setRotationFromEuler(0, 0, rotationAngle);
+        
+        // 应用位置偏移
+        const currentPos = weaponNode.position;
+        weaponNode.setPosition(currentPos.x + positionOffset.x, currentPos.y + positionOffset.y, currentPos.z);
     }
     
     // 设置特定武器和形状的旋转角度
@@ -1543,6 +1628,21 @@ export class BlockManager extends Component {
         console.log(`已更新默认形状 ${shapeId} 的旋转角度为: ${angle}度`);
     }
     
+    // 设置特定武器和形状的位置偏移
+    public setWeaponShapePositionOffset(weaponId: string, shapeId: string, offset: { x: number, y: number }) {
+        if (!this.WEAPON_SHAPE_POSITION_OFFSETS[weaponId]) {
+            this.WEAPON_SHAPE_POSITION_OFFSETS[weaponId] = {};
+        }
+        this.WEAPON_SHAPE_POSITION_OFFSETS[weaponId][shapeId] = offset;
+        console.log(`已更新武器 ${weaponId} 形状 ${shapeId} 的位置偏移为: (${offset.x}, ${offset.y})`);
+    }
+    
+    // 设置默认形状的位置偏移
+    public setDefaultShapePositionOffset(shapeId: string, offset: { x: number, y: number }) {
+        this.DEFAULT_SHAPE_POSITION_OFFSETS[shapeId] = offset;
+        console.log(`已更新默认形状 ${shapeId} 的位置偏移为: (${offset.x}, ${offset.y})`);
+    }
+    
     // 获取特定武器和形状的旋转角度
     public getWeaponShapeRotationAngle(weaponId: string, shapeId: string): number {
         if (this.WEAPON_SHAPE_ROTATION_ANGLES[weaponId]) {
@@ -1556,7 +1656,34 @@ export class BlockManager extends Component {
         return this.DEFAULT_SHAPE_ROTATION_ANGLES[shapeId] || 0;
     }
     
-    // 重新应用所有已放置方块的武器图标旋转
+    // 获取特定武器和形状的位置偏移
+    public getWeaponShapePositionOffset(weaponId: string, shapeId: string): { x: number, y: number } {
+        if (this.WEAPON_SHAPE_POSITION_OFFSETS[weaponId]) {
+            return this.WEAPON_SHAPE_POSITION_OFFSETS[weaponId][shapeId] || { x: 0, y: 0 };
+        }
+        return { x: 0, y: 0 };
+    }
+    
+    // 获取默认形状的位置偏移
+    public getDefaultShapePositionOffset(shapeId: string): { x: number, y: number } {
+        return this.DEFAULT_SHAPE_POSITION_OFFSETS[shapeId] || { x: 0, y: 0 };
+    }
+    
+    // 同时设置特定武器和形状的位置偏移和旋转角度
+    public setWeaponShapeTransform(weaponId: string, shapeId: string, offset: { x: number, y: number }, angle: number) {
+        this.setWeaponShapePositionOffset(weaponId, shapeId, offset);
+        this.setWeaponShapeRotationAngle(weaponId, shapeId, angle);
+        console.log(`已更新武器 ${weaponId} 形状 ${shapeId} 的完整变换: 偏移(${offset.x}, ${offset.y}), 旋转${angle}度`);
+    }
+    
+    // 同时设置默认形状的位置偏移和旋转角度
+    public setDefaultShapeTransform(shapeId: string, offset: { x: number, y: number }, angle: number) {
+        this.setDefaultShapePositionOffset(shapeId, offset);
+        this.setDefaultShapeRotationAngle(shapeId, angle);
+        console.log(`已更新默认形状 ${shapeId} 的完整变换: 偏移(${offset.x}, ${offset.y}), 旋转${angle}度`);
+    }
+    
+    // 重新应用所有已放置方块的武器图标位置和旋转
     public refreshAllWeaponIconRotations() {
         // 遍历所有已放置的方块
         if (this.placedBlocksContainer) {

+ 21 - 1
assets/scripts/FourUI/NavBarController.ts

@@ -1,4 +1,5 @@
 import { _decorator, Color, Component, Node, Sprite } from 'cc';
+import EventBus, { GameEvents } from '../Core/EventBus';
 const { ccclass, property } = _decorator;
 
 @ccclass('NavBarController')
@@ -23,6 +24,11 @@ export class NavBarController extends Component {
         // 检查Battle按钮是否被锁定(索引0)
         if (this.buttonLockStates[0]) {
             console.log('[NavBarController] Battle按钮被锁定,无法点击');
+            // 发送Toast事件显示锁定提示
+            EventBus.getInstance().emit(GameEvents.SHOW_TOAST, {
+                message: '暂未解锁该功能!',
+                duration: 2.0
+            });
             return;
         }
         this.switchTo(0); 
@@ -31,6 +37,11 @@ export class NavBarController extends Component {
         // 检查Upgrade按钮是否被锁定(索引2)
         if (this.buttonLockStates[2]) {
             console.log('[NavBarController] Upgrade按钮被锁定,无法点击');
+            // 发送Toast事件显示锁定提示
+            EventBus.getInstance().emit(GameEvents.SHOW_TOAST, {
+                message: '暂未解锁该功能!',
+                duration: 2.0
+            });
             return;
         }
         this.switchTo(1); 
@@ -39,7 +50,11 @@ export class NavBarController extends Component {
         // 检查shop按钮是否被锁定(索引1)
         if (this.buttonLockStates[1]) {
             console.log('[NavBarController] Shop按钮被锁定,无法点击');
-            // 可以在这里添加提示音效或震动效果
+            // 发送Toast事件显示锁定提示
+            EventBus.getInstance().emit(GameEvents.SHOW_TOAST, {
+                message: '暂未解锁该功能!',
+                duration: 2.0
+            });
             return;
         }
         this.switchTo(2); 
@@ -48,6 +63,11 @@ export class NavBarController extends Component {
         // 检查Skill按钮是否被锁定(索引3)
         if (this.buttonLockStates[3]) {
             console.log('[NavBarController] Skill按钮被锁定,无法点击');
+            // 发送Toast事件显示锁定提示
+            EventBus.getInstance().emit(GameEvents.SHOW_TOAST, {
+                message: '暂未解锁该功能!',
+                duration: 2.0
+            });
             return;
         }
         this.switchTo(3);

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.