最新文章
泰课新年学课蛇来运转欢度春节活动
02-01 20:25
共庆2024圣诞、元旦泰课双蛋活动
12-16 10:21
泰课共庆75周年国庆活动!
10-05 21:24
暑假双月联动学习计划 7月15 - 8月21日
07-14 23:09
泰课在线劳动光荣,勤学快乐之五月勤学季活动
04-30 21:19
2024年青春绽放开学季活动
03-11 13:01
Unity3d 根据布线,自动生成碰撞墙
在自主开发项目时,若要限制玩家的可移动范围,手动逐个摆放碰撞体(Collider)的操作着实繁琐。为此,我编写了一个工具,只需绘制好路径,就能一键生成碰撞体。
效果展示
绘制路径
此处可插入绘制路径的相关图片(原文未提供,可后续补充)。
生成碰撞墙的效果
此处可插入生成碰撞墙效果的相关图片(原文未提供,可后续补充)。
从效果来看,该工具比手动摆放方便许多,下面为你详细介绍实现代码。
代码实现
核心脚本 FenceWall.cs
using UnityEngine;
using System;
public class FenceWall : MonoBehaviour
{
/// <summary>
/// 尺寸 x:宽度 y:高度
/// </summary>
public Vector2 Size = new Vector2(1, 10);
/// <summary>
/// 补漏尺寸
/// </summary>
public float BareSize = 0.5f;
/// <summary>
/// 路径父对象
/// </summary>
public Transform PathParent;
/// <summary>
/// 是否闭合路径
/// </summary>
public bool IsClose = false;
/// <summary>
/// 是否绘制路径
/// </summary>
public bool IsGizmos = false;
/// <summary>
/// 创建碰撞墙
/// </summary>
public void CreateFenceWall()
{
if (PathParent == null) return;
// 查找已生成的碰撞墙
Transform target = transform.Find("FenceWall");
if (target != null)
{
DestroyImmediate(target.gameObject);
}
// 生成新的碰撞墙父对象
GameObject parent = new GameObject("FenceWall");
parent.transform.localPosition = Vector3.zero;
parent.transform.localScale = Vector3.one;
// 遍历路径
TraversePath((trans1, trans2) =>
{
CreateWall(trans1, trans2, (go) =>
{
go.transform.parent = parent.transform;
});
});
parent.transform.parent = transform;
}
/// <summary>
/// 生成碰撞墙
/// </summary>
/// <param name="pos1">起始点变换</param>
/// <param name="pos2">结束点变换</param>
/// <param name="onCreate">创建完成后的回调</param>
private void CreateWall(Transform pos1, Transform pos2, Action<GameObject> onCreate)
{
GameObject go = new GameObject($"{pos1.name}->{pos2.name}");
float dis = Vector3.Distance(pos1.position, pos2.position);
// 添加盒型碰撞体
BoxCollider bc = go.AddComponent<BoxCollider>();
bc.size = new Vector3(dis + BareSize, Size.y, Size.x);
bc.center = new Vector3(0, Size.y / 2, 0);
go.transform.parent = transform;
go.layer = 1 << LayerMask.GetMask("Wall");
// 计算坐标
go.transform.position = pos1.position + (pos2.position - pos1.position).normalized * (dis * 0.5f);
// 计算旋转
float z = pos1.position.z - pos2.position.z;
float angle = Mathf.Asin(z / dis) * Mathf.Rad2Deg;
// 计算旋转朝向
float dir = pos1.position.x - pos2.position.x > 0 ? -1 : 1;
go.transform.rotation = Quaternion.Euler(new Vector3(0, angle * dir, 0));
if (onCreate != null)
onCreate(go);
}
/// <summary>
/// 遍历路径
/// </summary>
/// <param name="onTraverse">遍历回调</param>
private void TraversePath(Action<Transform, Transform> onTraverse)
{
int childCount = PathParent.childCount;
if (childCount > 2)
{
for (int i = 0; i < childCount; i++)
{
Transform child1 = PathParent.GetChild(i);
Transform child2 = null;
if (i == childCount - 1)
{
if (IsClose)
child2 = PathParent.GetChild(0);
else
break;
}
else
child2 = PathParent.GetChild(i + 1);
if (onTraverse != null)
onTraverse(child1, child2);
}
}
}
/// <summary>
/// 绘制路径
/// </summary>
private void OnDrawGizmos()
{
if (!IsGizmos) return;
if (PathParent == null) return;
TraversePath((trans1, trans2) =>
{
Gizmos.DrawLine(trans1.position, trans2.position); // 原代码有误,应是 trans2 而非 trans1
});
}
}
编辑器脚本 FenceWallInspector.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(FenceWall))]
public class FenceWallInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
FenceWall fw = (FenceWall)target;
if (GUILayout.Button("Create Wall"))
{
fw.CreateFenceWall();
}
}
}
使用方法
将 FenceWall 脚本挂载到一个游戏对象(GameObject)上,设置好路径(即 PathParent),最后在 Inspector 面板中点击 “Create Wall” 按钮,即可自动生成碰撞墙。
场景应用展示
在实际场景中使用该工具,不仅省事,而且生成的碰撞墙规整有序。此处可插入场景应用的相关图片(原文未提供,可后续补充)。