break prefab instance的原理

本文探讨了Unity中Prefab的实例化机制及其对工程的影响。重点介绍了如何确保Prefab实例化后不会影响原始Prefab及其资源,提出了两种解决方案:一是创建新的Prefab副本;二是解除Prefab实例与原始Prefab之间的链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

break prefab instance的原理

prefab可无限apply:

如果把一个模块做成了prefab,这个prefab可能在同一个scene中添加多个,甚至添加到了多个scene中。设所有这些实例为instance(1),instance(2),...,instance(n),那么我们应该保证对于任何一个instance(i)来说,对它点apply都不会对工程造成任何影响。那么假如做不到这一点,那么或把prefab及其中引用的资源完全拷贝一份做成一个新的prefab,或者直接break prefab instance使其成为独立节点

posted on 2017-01-05 10:53 时空观察者9号 阅读(...) 评论(...) 编辑 收藏

你别乱改,using System.Collections.Generic; using UnityEngine; using UnityEngine.Tilemaps; namespace gridstudy { public class GridManager : MonoBehaviour { [SerializeField] private GridConfig _config; [SerializeField] private Tilemap _terrainTilemap; [SerializeField] private TileBase _defaultTile; private GridCell[,] _gridData; private Dictionary<Vector2Int, List<GridObject>> _objectsMap = new(); private Dictionary<GridObjectType, GameObject> _prefabMap; public int GridWidth => _gridData?.GetLength(0) ?? 0; public int GridHeight => _gridData?.GetLength(1) ?? 0; public GridConfig Config => _config; void Awake() { // 初始化prefab映射 _prefabMap = new Dictionary<GridObjectType, GameObject> { { GridObjectType.Terrain, _config.TerrainPrefab }, { GridObjectType.Structure, _config.StructurePrefab }, { GridObjectType.Chess, _config.ChessPrefab }, { GridObjectType.Token, _config.TokenPrefab } }; } public void GenerateGrid(int width, int height) { _gridData = new GridCell[width, height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { _gridData[x, y] = new GridCell(new Vector2Int(x, y)); Vector3Int tilePos = new Vector3Int(x, y, 0); _terrainTilemap.SetTile(tilePos, _defaultTile); } } } public GridCell GetCellData(Vector2Int coord) { if (coord.x < 0 || coord.x >= GridWidth || coord.y < 0 || coord.y >= GridHeight) { Debug.LogWarning($"Invalid coordinate: {coord}"); return null; } return _gridData[coord.x, coord.y]; } public CustomTile GetTileAtPosition(Vector2Int gridPos) { GridCell cell = GetCellData(gridPos); if (cell == null || cell.terrain == null) return null; TerrainConfig config = _config.GetTerrainConfig(cell.terrain.type); return config?.tile; } public void PlaceObject<T>(GridObjectType type, Vector2Int gridPos, T data) where T : ScriptableObject { if (!_prefabMap.TryGetValue(type, out GameObject prefab)) { Debug.LogError($"No prefab found for type: {type}"); return; } // 验证坐标有效性 GridCell cell = GetCellData(gridPos); if (cell == null) { Debug.LogError($"Invalid grid position: {gridPos}"); return; } GameObject instance = Instantiate(prefab, GridToWorld(gridPos), Quaternion.identity); GridObject gridObject = instance.GetComponent<GridObject>(); if (gridObject == null) { Debug.LogError($"Prefab for {type} is missing GridObject component"); Destroy(instance); return; } gridObject.GridPosition = gridPos; // 更新数据层 switch (type) { case GridObjectType.Terrain: cell.terrain = data as TerrainData; var terrainObj = instance.GetComponent<TerrainObject>(); if (terrainObj != null) { TerrainConfig config = _config.GetTerrainConfig(cell.terrain.type); terrainObj.Initialize(cell.terrain, config?.Sprite); } UpdateTileAtPosition(gridPos,config?.tile); break; case GridObjectType.Structure: cell.structure = data as StructureData; break; case GridObjectType.Chess: cell.chess = data as ChessData; break; case GridObjectType.Token: cell.tokens.Add(data as TokenData); break; default: Debug.LogError($"Unhandled object type: {type}"); break; } // 更新对象映射 if (!_objectsMap.ContainsKey(gridPos)) _objectsMap[gridPos] = new List<GridObject>(); _objectsMap[gridPos].Add(gridObject); // 触发对象放置事件 gridObject.OnPlaced(); } // 添加 Tilemap 更新方法 private void UpdateTileAtPosition(Vector2Int gridPos, CustomTile tile) { if (_terrainTilemap == null) return; Vector3Int tilePos = new Vector3Int(gridPos.x, gridPos.y, 0); _terrainTilemap.SetTile(tilePos, tile); } // 坐标转换(网格坐标->世界坐标) private Vector3 GridToWorld(Vector2Int gridPos) { return new Vector3(gridPos.x + 0.5f, gridPos.y + 0.5f, 0); } } }从这个开始,只把功能修改好,其他的别做,上一次生成作废
07-15
// SkillManager.ts import { _decorator, Component, Node, Prefab, Vec3, v3, instantiate, Tween, tween, find } from ‘cc’; const { ccclass, property } = _decorator; import { GameManager } from ‘./GameManager’; // 导入GameManager import { MonsterController } from ‘./MonsterController’; // 导入MonsterController @ccclass(‘SkillManager’) export class SkillManager extends Component { @property({type: [Prefab]}) private skillsPrefabs: Prefab[] = []; // 技能特效节点预制体数组 // 添加对GameManager的引用 @property({type: Node}) private gameManagerNode: Node; // 挂载GameManager的节点 private gameManager: GameManager | null = null; // 存储当前激活的技能实例 private activeWanJianJueSkills: Node[] = []; //存储万剑诀实例 private activeDuJieShuSkills: Node[] = []; // 存储渡劫术实例 // 英雄节点引用 private heroNode: Node | null = null; // 万剑诀生成计时器 private wanJianJueSpawnTimer: number = 0; private wanJianJueSpawnInterval: number = 0.2; // 0.2秒生成间隔 private wanJianJueSpawnCount: number = 0; // 已生成数量 private wanJianJueTotalCount: number = 10; // 总生成数量 // 万剑诀攻击范围(像素) private readonly WAN_JIAN_JUE_ATTACK_RANGE: number = 30; protected onLoad(): void { // 获取GameManager组件 if (this.gameManagerNode) { this.gameManager = this.gameManagerNode.getComponent(GameManager); // 获取英雄节点 this.heroNode = this.gameManager.HeroNode; } } // 每帧更新 update(deltaTime: number) { // 更新万剑诀生成计时器 if (this.wanJianJueSpawnCount > 0 && this.wanJianJueSpawnCount < this.wanJianJueTotalCount) { this.wanJianJueSpawnTimer += deltaTime; // 检查是否达到生成间隔 if (this.wanJianJueSpawnTimer >= this.wanJianJueSpawnInterval) { this.wanJianJueSpawnTimer = 0; this.createWanJianJueInstance(); } } } //按键启动万剑诀 public buttenWanJianJue(){ // 获取最近的怪兽 let nearestMonster = this.getNearestMonsterForSkill(); if (nearestMonster) { // 重置生成参数 this.wanJianJueSpawnCount = 0; this.wanJianJueSpawnTimer = 0; // 立即生成第一个实例 this.createWanJianJueInstance(); } else { console.warn(“没有找到有效的怪兽作为万剑诀落点”); } } /** * 获取最近的怪兽节点 * @returns 最近的怪兽节点或null / private getNearestMonsterForSkill(): Node | null { if (!this.gameManager) { console.warn(“GameManager未设置”); return null; } return this.gameManager.getNearestAliveMonster(); } /* * 创建单个万剑诀实例 / private createWanJianJueInstance(): void { // 1. 找到万剑诀预制体 const wanJianJuePrefab = this.skillsPrefabs.find(prefab => prefab.name === “WanJianJue”); if (!wanJianJuePrefab) { console.error(“万剑诀预制体未找到!”); return; } // 确保英雄节点有效 if (!this.heroNode || !this.heroNode.isValid) { console.warn(“英雄节点无效,无法创建万剑诀实例”); return; } // 创建实例 const instance = instantiate(wanJianJuePrefab); // 获取英雄位置 const heroPos = this.heroNode.position.clone(); // 位置生成参数 const minX = heroPos.x - 350 + 4700.6; const maxX = heroPos.x + 350 + 4700.6; const minY = -350 - 1850.6; const maxY = -50 -185*0.6; const minDistance = 80; // 距离英雄的最小距离 let position: Vec3; let attempts = 0; const maxAttempts = 10; // 最大尝试次数 // 尝试找到一个满足条件的位置 do { attempts++; // 在指定范围内生成随机位置 const randomX = Math.random() * (maxX - minX) + minX; const randomY = Math.random() * (maxY - minY) + minY; position = v3(randomX, randomY, heroPos.z); // 检查是否满足距离要求 const distanceToHero = Vec3.distance(position, heroPos); // 如果满足距离要求或达到最大尝试次数 if (distanceToHero >= minDistance || attempts >= maxAttempts) { break; } } while (true); // 设置位置 instance.setPosition(position); // 添加到场景 this.heroNode.parent.addChild(instance); // 添加到激活技能列表 this.activeWanJianJueSkills.push(instance); // 增加生成计数 this.wanJianJueSpawnCount++; // 0.5秒后检测伤害(基于该实例位置) this.scheduleOnce(() => { this.detectWanJianJueDamageForInstance(position); // 0.3秒后删除该实例(总共0.8秒) this.scheduleOnce(() => { if (instance.isValid) { instance.destroy(); // 从激活列表中移除 const index = this.activeWanJianJueSkills.indexOf(instance); if (index !== -1) { this.activeWanJianJueSkills.splice(index, 1); } } }, 0.25); }, 0.5); } /** * 为单个万剑诀实例检测伤害 * @param position 实例位置 / private detectWanJianJueDamageForInstance(position: Vec3): void { if (!this.gameManager) { console.warn(“GameManager未设置,无法检测技能伤害”); return; } // 获取所有怪物 const monsters = this.gameManager.getAllMonsters(); for (const monster of monsters) { if (!monster || !monster.isValid) continue; const controller = monster.getComponent(MonsterController); if (!controller || controller.isDie) continue; // 计算距离(基于怪兽尺寸半径) const distance = Vec3.distance(monster.position, position); const monsterRadius = controller.MonsterSize; // 获取怪兽尺寸半径 // 计算总攻击范围 = 怪物半径 + 30像素攻击范围 const totalRange = monsterRadius + this.WAN_JIAN_JUE_ATTACK_RANGE; // 如果距离小于等于怪兽尺寸半径,造成伤害 if (distance <= totalRange) { // 对怪物造成伤害 controller.takeDamage(60); } } } /* * 清空所有激活的技能实例 / private clearActiveSkills(): void { for (const skill of this.activeWanJianJueSkills) { if (skill.isValid) { skill.destroy(); } } this.activeWanJianJueSkills = []; } /* * 启动横扫千军技能 / public buttenHengSaoQianJun() { // 在英雄位置创建横扫千军特效 this.startHengSaoQianJun(); } /* * 创建横扫千军技能 / private startHengSaoQianJun(): void { // 1. 找到横扫千军预制体 const hengSaoQianJunPrefab = this.skillsPrefabs.find(prefab => prefab.name === “HengSaoQianJun1”); if (!hengSaoQianJunPrefab) { console.error(“横扫千军预制体未找到!”); return; } // 确保英雄节点有效 if (!this.heroNode || !this.heroNode.isValid) { console.warn(“英雄节点无效,无法创建横扫千军特效”); return; } // 创建实例 const instance = instantiate(hengSaoQianJunPrefab); // 获取英雄朝向(Scale.x为正表示朝左,为负表示朝右) const heroScaleX = this.heroNode.scale.x; const isHeroFacingLeft = heroScaleX > 0; // 设置偏移量 - 根据英雄朝向调整X方向 const offsetX = isHeroFacingLeft ? 364 : -364; const offset = new Vec3(offsetX, 25, 0); // 计算最终位置 const heroPos = this.heroNode.position.clone(); const position = new Vec3( heroPos.x + offset.x, heroPos.y + offset.y, heroPos.z + offset.z ); instance.setPosition(position); // 调整技能朝向 - 与英雄朝向一致 const currentScale = instance.scale; if (isHeroFacingLeft) { // 英雄朝左,技能朝左(Scale.x为正) instance.setScale(new Vec3(Math.abs(currentScale.x), currentScale.y, currentScale.z)); } else { // 英雄朝右,技能朝右(Scale.x为负) instance.setScale(new Vec3(-Math.abs(currentScale.x), currentScale.y, currentScale.z)); } // 添加到场景 this.node.addChild(instance); // 添加到激活技能列表 this.activeWanJianJueSkills.push(instance); // 添加伤害检测 this.scheduleOnce(() => { this.detectHengSaoDamage(position); }, 0.7); // 0.7秒后检测伤害 // 1.7秒后删除技能实例 this.scheduleOnce(() => { if (instance.isValid) { instance.destroy(); // 从激活列表中移除 const index = this.activeWanJianJueSkills.indexOf(instance); if (index !== -1) { this.activeWanJianJueSkills.splice(index, 1); } } }, 1.6667); } /* * 检测横扫千军伤害 * @param position 技能位置 / private detectHengSaoDamage(position: Vec3): void { if (!this.gameManager) { console.warn(“GameManager未设置,无法检测技能伤害”); return; } // 获取英雄朝向(Scale.x为正表示朝左,为负表示朝右) const heroScaleX = this.heroNode.scale.x; const isHeroFacingLeft = heroScaleX > 0; // 获取英雄位置 const heroPos = this.heroNode.position.clone(); // 定义伤害区域参数 const damageLength = 400; // 伤害区域长度(像素) const damageWidth = 200; // 伤害区域宽度(像素) const damageAmount = 80; // 伤害值80点 // 计算伤害区域边界 let minX: number, maxX: number; let minY: number, maxY: number; if (isHeroFacingLeft) { // 英雄朝左,伤害区域向左延伸 minX = heroPos.x - damageLength; maxX = heroPos.x; } else { // 英雄朝右,伤害区域向右延伸 minX = heroPos.x; maxX = heroPos.x + damageLength; } // Y轴范围(以英雄位置为中心) minY = heroPos.y - damageWidth / 2; maxY = heroPos.y + damageWidth / 2; // 获取所有怪物 const monsters = this.gameManager.getAllMonsters(); for (const monster of monsters) { if (!monster || !monster.isValid) continue; const controller = monster.getComponent(MonsterController); if (!controller || controller.isDie) continue; // 获取怪物位置 const monsterPos = monster.position; // 检查怪物是否在伤害区域内 const inXRange = monsterPos.x >= minX && monsterPos.x <= maxX; const inYRange = monsterPos.y >= minY && monsterPos.y <= maxY; if (inXRange && inYRange) { // 对怪物造成伤害 controller.takeDamage(damageAmount); } } } // 按键启动渡劫术 public buttonDuJieShu() { // 获取所有存活的怪兽 const allMonsters = this.getAllAliveMonsters(); if (allMonsters.length > 0) { this.startDuJieShu(allMonsters); } else { console.warn(“没有存活的怪兽作为渡劫术目标”); } } /* * 获取所有存活的怪兽 * @returns 存活怪兽节点数组 / private getAllAliveMonsters(): Node[] { if (!this.gameManager) { console.warn(“GameManager未设置”); return []; } return this.gameManager.getAllMonsters(); } /* * 启动渡劫术技能 * @param targetMonsters 目标怪物节点数组 / private startDuJieShu(targetMonsters: Node[]): void { // 1. 找到渡劫术预制体 const duJieShuPrefab = this.skillsPrefabs.find(prefab => prefab.name === “DuJieShu-1”); if (!duJieShuPrefab) { console.error(“渡劫术预制体未找到!”); return; } // 2. 为每个怪物创建渡劫术实例 for (const monster of targetMonsters) { if (!monster || !monster.isValid) continue; const controller = monster.getComponent(MonsterController); if (!controller || controller.isDie) continue; // 创建渡劫术实例 const instance = instantiate(duJieShuPrefab); // 设置位置为怪物位置 instance.setPosition(monster.position.clone()); // 添加到场景 this.heroNode.parent.addChild(instance); // 添加到激活技能列表 this.activeDuJieShuSkills.push(instance); // 3. 立即对目标怪物造成伤害 this.applyDuJieShuDamage(monster); // 4. 0.4667秒后删除技能实例 this.scheduleOnce(() => { if (instance.isValid) { instance.destroy(); // 从激活列表中移除 const index = this.activeDuJieShuSkills.indexOf(instance); if (index !== -1) { this.activeDuJieShuSkills.splice(index, 1); } } }, 0.4667); } } /* * 应用渡劫术伤害 * @param targetMonster 目标怪物节点 */ private applyDuJieShuDamage(targetMonster: Node): void { if (!targetMonster || !targetMonster.isValid) return; const controller = targetMonster.getComponent(MonsterController); if (!controller || controller.isDie) return; // 直接对怪物造成20点伤害 controller.takeDamage(10); console.log(渡劫术对 ${targetMonster.name} 造成20点伤害!); } // 组件销毁时清理资源 protected onDestroy(): void { this.clearActiveSkills(); } }新加一个功能,横扫千军在计算伤害的同时(此时巨剑砸到地面刚好适合)屏幕抖动一次(和GameManager.ts里的暴击一样)
最新发布
07-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值