Unity中如何让盒子精灵精准落在鼠标点击位置且无倾斜?
Unity Canvas盒子精灵倾斜问题排查与修复
我正在开发一个带Canvas的Unity项目,尝试创建盒子对象。盒子能正常创建,但放置时精灵会出现倾斜,效果如下:
现有代码逻辑
射线检测与放置触发
用户点击Canvas时,通过相机发射射线获取鼠标位置,命中对象后验证位置并创建盒子:
Ray ray = _camera.ScreenPointToRay(Input.mousePosition); if(Physics.Raycast(ray, out RaycastHit hitInfo, Mathf.Infinity)) { if (_CheckCanDropObject(ray, hitInfo)) { BuildingController.CreateBox(new Vector3(hitInfo.point.x, hitInfo.point.y, WHConstants.AboveDefaultZ), buildingInventoryController.currentBlock.BlockSprite); } }
盒子创建流程
位置验证通过后,调用CreateBox遍历建筑子对象找到平面图,通过工厂模式创建盒子并添加到命令队列:
public static void CreateBox(Vector3 position, UnityEngine.Sprite sprite) { List<ICommand> commands = new List<ICommand>(); foreach (CreatorItem child in GetBuilding().children) { if (child.name.Contains(WHConstants.FLOOR_PLAN)) { CreatorItem floorPlan = child; float height = floorPlan.GetComponent<IHasDimension>().Dimension.Height; string name = NamingController.GetName(WHConstants.Box, floorPlan.uiItem.Foldout.Children()); CreatorItemCreateCommand createCommand = new CreatorItemCreateCommand(new CreatorBoxFactory(position, sprite, height), name); ICommand createAndAddParentCommand = new CreateCreatorItemWithParentCommand(createCommand, floorPlan.Id); commands.Add(createAndAddParentCommand); } } ICommand createBox = new MultipleCommand(commands); UndoRedo.AddAndExecuteCommand(createBoxs); // 注意:此处变量名疑似笔误,应为createBox DeselectAll(); CreatorUIController.UnSavedProgress = true; }
盒子工厂实现
CreatorBoxFactory负责生成盒子GameObject,包含SpriteRenderer和碰撞体:
public class CreatorBoxFactory : ICreatorItemFactory { private Vector3 _position; private Sprite _sprite; private float _height; public CreatorBoxFactory(Vector3 position, Sprite sprite, float height) { _height = height; _position = position; _sprite = sprite; } public CreatorItem Create(string name, bool createGO = true) { Box item; GameObject Box = null; if (createGO) { UIItem uiItem = new BoxUIFactory().Create(name); Box = CreateBox(); item = new Box(Box, uiItem); } else { item = new Box(null, null); } item.SetPosition(new Vector3(_position.x, _position.y, 0)); item.SetDimension(WHConstants.DefaultBoxLength, _height, WHConstants.DefaultBox2DHeight); item.SetName(name); if (item.uiItem != null) { item.uiItem._delegate = item; } if (createGO) { item.SetTransformer(); } return item; } private GameObject CreateBox() { GameObject boxGameObject = new GameObject(); SpriteRenderer spriteRenderer = boxGameObject.AddComponent<SpriteRenderer>(); spriteRenderer.sprite = _sprite; Vector2 unit = ConvertCoordinate.PixelToUnit(spriteRenderer.sprite); BoxCollider lineCollider = boxGameObject.AddComponent<BoxCollider>(); lineCollider.transform.parent = spriteRenderer.transform; lineCollider.size = new Vector3(unit.x, unit.y, 1f); lineCollider.center = new Vector3(0.0f, 0.0f, 0.0f); boxGameObject.tag = WHConstants.METABLOCK; boxGameObject.transform.position = _position; boxGameObject.transform.localScale = new Vector3(WHConstants.DefaultBoxLength / unit.x, WHConstants.DefaultBox2DHeight / unit.y, 1); return boxGameObject; } }
问题分析与修复方案
倾斜原因排查
- 父对象旋转继承:盒子被父化到
floorPlan对象下,如果floorPlan本身带有旋转角度,盒子会继承该旋转,导致精灵倾斜。 - 碰撞体父化错误:将
BoxCollider挂载到SpriteRenderer子对象上,若SpriteRenderer有旋转/缩放偏移,会带动碰撞体和视觉表现异常。 - 未明确重置旋转:创建盒子时未强制重置旋转状态,可能继承场景或父对象的默认旋转值。
修复步骤
强制重置盒子旋转:在创建盒子GameObject后,立即重置旋转为无角度状态:
private GameObject CreateBox() { GameObject boxGameObject = new GameObject(); // 重置根对象旋转,避免继承默认或父对象旋转 boxGameObject.transform.rotation = Quaternion.identity; SpriteRenderer spriteRenderer = boxGameObject.AddComponent<SpriteRenderer>(); spriteRenderer.sprite = _sprite; // ... 其余代码保持不变 }修正碰撞体挂载层级:将碰撞体直接挂载在盒子根对象上,避免受SpriteRenderer的变换影响:
private GameObject CreateBox() { GameObject boxGameObject = new GameObject(); boxGameObject.transform.rotation = Quaternion.identity; SpriteRenderer spriteRenderer = boxGameObject.AddComponent<SpriteRenderer>(); spriteRenderer.sprite = _sprite; Vector2 unit = ConvertCoordinate.PixelToUnit(spriteRenderer.sprite); // 移除lineCollider.transform.parent = spriteRenderer.transform; BoxCollider lineCollider = boxGameObject.AddComponent<BoxCollider>(); lineCollider.size = new Vector3(unit.x, unit.y, 1f); lineCollider.center = new Vector3(0.0f, 0.0f, 0.0f); // ... 其余代码保持不变 }父化后重置局部旋转:在将盒子父化到
floorPlan后,确保盒子的局部旋转为无角度状态(可在CreateCreatorItemWithParentCommand的执行逻辑中添加):// 父化操作完成后添加 createdItem.transform.localRotation = Quaternion.identity;适配Canvas坐标系(可选):如果是纯UI Canvas项目,建议使用2D方式获取点击位置,替代3D射线检测,避免坐标系偏差:
if (Input.GetMouseButtonDown(0)) { if (RectTransformUtility.ScreenPointToLocalPointInRectangle( yourCanvasRectTransform, Input.mousePosition, _camera, out Vector2 localPoint)) { Vector3 worldPosition = yourCanvasRectTransform.TransformPoint(localPoint); worldPosition.z = WHConstants.AboveDefaultZ; if (_CheckCanDropObject(worldPosition)) { BuildingController.CreateBox(worldPosition, buildingInventoryController.currentBlock.BlockSprite); } } }
精准定位补充
要确保盒子精准落在鼠标点击位置:
- 检查Sprite导入设置,将
Pivot设为中心(0.5,0.5),保证视觉中心点与对象原点对齐 - 确保点击位置转换时,坐标系保持一致(3D世界空间或Canvas局部空间)
- 避免对创建后的盒子位置进行二次偏移操作
内容的提问来源于stack exchange,提问作者Rasik
相关产品推荐
相关产品推荐

