Build System
下载完毕,导入。
下面这个脚本,直接挂载到一个空物体,新建一个 3D物体 立方体,将其设置为 BuildGameObject变量的值(如下图所示)。新建一个 按钮button,来触发placeBool函数即可 实现放置基本的物体。
using UnityEngine;
using BuildSystem;
public class BuildManage : MonoBehaviour {
public Material ghostMaterial;//放置物体 时候的 材质
private Material goMaterial;
//**********************************************************************************************
[Header("Input Settings")]
[Tooltip("Key to press to enable the builder mode. This is also used by ObjectSelector")]
public KeyCode toggleKey = KeyCode.E;
[Tooltip("Key to press to place a item in the scene")]
public KeyCode placeKey = KeyCode.Mouse0;
[Tooltip("Key to press rotate (forward) the object based on snapRotaionDeg")]
public KeyCode positiveRotateKey = KeyCode.Mouse1;
[Tooltip("Key to press rotate (backward) the object based on snapRotaionDeg")]
public KeyCode negativeRotateKey = KeyCode.None;
[Header("More Settings")]
[Tooltip("Disable the remover script when placer is active. Note: remover must be next to this script")]
public bool shouldToggleRemover = true;
[Tooltip("Max distance from camera where you can place objects")]
public float maxPlaceDistance = 100f;
/****************************************************
* Editor Interface
* *************************************************/
[Tooltip("Camera used to raycast and find place position, if empty the script will try to use the main camera")]
public Camera cam;
public LayerMask movementMask;//设置 LayerMask
public GameObject buildGameObject;//建设的物体
/****************************************************
* Variables
* *************************************************/
bool isActive = true;
bool canPlace = false;
bool mouseIsNotOnUI = true;
//object rotation
float objectSnapCurrentRotaion = 0;
//object pivot
bool usingFakePivot = false;
/****************************************************
* Events
* *************************************************/
public delegate void BuildEvent();
public event BuildEvent OnGhostObjectCreation;
public event BuildEvent OnGhostObjectDestroy;
public event BuildEvent OnGhostObjectPlace;
/****************************************************
* Components & references
* *************************************************/
Transform ghostObjInstance;
Transform myTransform;
// Use this for initialization
void Start () {
if (cam == null)
cam = CameraManager.instance.nowCamera;
myTransform = transform;
}
void Update()
{
//点击鼠标左键
if (Input.GetMouseButtonDown(0) && canPlace == true)
{
PlaceGhostObject();
}
}
private void FixedUpdate()
{
//update ghost object position
if (canPlace && ghostObjInstance != null)
{
MoveGhostObject();
}
}
//由按钮触发
public void placeBool()
{
canPlace = true;
CreateGhostObject();
}
/****************************************************
* Ghost Object Creation & Movement & Place
* *************************************************/
/// <summary>
/// Create ghost object
/// </summary>
void CreateGhostObject()
{
DestroyGhostObject();
/*if (objectToPlace == null)
{
Debug.LogError("No item to instantiate! Aborting ghost creation.");
return;
}*/
ghostObjInstance = Instantiate(buildGameObject, myTransform.position, Quaternion.identity).GetComponent<Transform>();
goMaterial =ghostObjInstance.transform.GetComponent<MeshRenderer>().material;
ghostObjInstance.transform.GetComponent<MeshRenderer>().material = ghostMaterial;
ghostObjInstance.transform.gameObject.layer = 0;//设置 新建物体 时候,的预览 幻影 物体 的层级。使其 不BUG
//reset old object rotation
//if (resetRotationAfterPlace)
// objectSnapCurrentRotaion = 0;
//check where is the pivot, if it is not in the base create a fake one
usingFakePivot = false;
Vector3 pivotOffsetExtra;
bool objPivotIsBase = isPivotInBase(ghostObjInstance, out pivotOffsetExtra);
//create a fake pivot if the real one is not in base
if (!objPivotIsBase)
{
ghostObjInstance = CreateBasePivot(ghostObjInstance, pivotOffsetExtra);
usingFakePivot = true;
}
if (OnGhostObjectCreation != null)
{
OnGhostObjectCreation();
}
}
/// <summary>
/// Move the ghost object
/// </summary>
void MoveGhostObject()
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//射线只碰撞到 有碰撞体的物体 ,距离为 100 ,并且 LayerMask层级为 Floor的物体
if (Physics.Raycast(ray, out hit, maxPlaceDistance, movementMask))
{
//set object position to hit point
Vector3 pos = hit.point;
ghostObjInstance.transform.position = pos;
AlignGhostToSurface(ghostObjInstance, hit.normal);
}
}
/// <summary>
/// Place the object in the scene
/// </summary>
void PlaceGhostObject()
{
if (ghostObjInstance != null)
{
Debug.Log("Created: " + ghostObjInstance.name);
if (usingFakePivot) // remove fake pivot if using one
{
ghostObjInstance = ghostObjInstance.GetComponent<PivotHelper>().DeletePivot();
}
//ghostObjInstance.transform.GetComponent<Renderer>().material = goMaterial;
//place real object in scene
GameObject go = Instantiate(buildGameObject, ghostObjInstance.position, ghostObjInstance.rotation);
go.layer = 8;//设置 物体的层级
//Debug.Log("ghostObjInstance.transform.childCount:"+ ghostObjInstance.transform.childCount);
Destroy(ghostObjInstance.gameObject);
if (OnGhostObjectPlace != null)
{
OnGhostObjectPlace();
}
//CreateGhostObject(); //create a new ghost object
}
else Debug.LogError("Unable to spawn object, ghost reference is null!");
canPlace = false;
}
/// <summary>
/// Remove ghost object from scene
/// </summary>
void DestroyGhostObject()
{
if (ghostObjInstance != null)
{
Destroy(ghostObjInstance.gameObject);
/*if (OnGhostObjectDestroy != null)
{
OnGhostObjectDestroy();
}*/
}
}
/****************************************************
* Pivot helpers 中心点
* *************************************************/
/// <summary>
/// Create a pivot parent to better place the object
/// </summary>
/// <param name="item">Item to use</param>
/// <param name="renderer">Renderer of the item</param>
/// <param name="pivotOffset">Offset of the pivot</param>
/// <returns></returns>
Transform CreateBasePivot(Transform item, Vector3 pivotOffset)
{
var normMesh = item.GetComponentInChildren<MeshRenderer>();
var skinMesh = item.GetComponentInChildren<SkinnedMeshRenderer>();
if (normMesh == null && skinMesh == null)
{
Debug.LogError("No renderers found!");
return null;
}
GameObject pivotG = new GameObject("Temp_Ghost_Pivot_Parent"); // create parent
Transform pivotT = pivotG.transform;
pivotG.AddComponent<PivotHelper>(); // add helper class to remove the pivot when object is spawned
//get mesh center
Vector3 meshCenter = (normMesh != null) ? normMesh.bounds.extents : skinMesh.bounds.extents;
// apply pivot delta
meshCenter.x = pivotOffset.x;
meshCenter.z = pivotOffset.z;
meshCenter.y += pivotOffset.y;
item.SetParent(pivotT); // set the current object as parent
item.localPosition = meshCenter; // move the object and leave the parent object in the pivot position
return pivotT;
}
/// <summary>
/// Check if the object pivot is in center or not.
/// This function returns the pivot Offset (can be Vector3.zero)
/// </summary>
/// <param name="item">Item to use</param>
/// <param name="renderer">Renderer attached to the item</param>
/// <param name="pivotOffset">Offset of the pivot to be in base</param>
/// <returns></returns>
bool isPivotInBase(Transform item, out Vector3 pivotOffset)
{
var normMesh = item.GetComponentInChildren<MeshRenderer>();
var skinMesh = item.GetComponentInChildren<SkinnedMeshRenderer>();
if (normMesh == null && skinMesh == null)
{
Debug.LogError("No mesh renderer found!");
pivotOffset = Vector3.zero;
return false;
}
var pivotMargin = (normMesh != null) ? normMesh.bounds.extents.y * 2 / 3 : skinMesh.bounds.extents.y * 2 / 3; //set the base pivot margin.
//Its' height must be lower than obj center * 2/3
Vector3 delta = item.position - ((normMesh != null) ? normMesh.bounds.center : skinMesh.bounds.center);
if (delta.magnitude >= pivotMargin && delta.y < 0) //delta.y < 0 fix issues that not centerd pivots above the object center were taken as base pivots
{
pivotOffset = Vector3.zero;
return true;
}
else
{
pivotOffset = delta; // save pivot delta to use to create a fake pivot
return false;
}
}
/****************************************************
* Ghost Object Alignament
* *************************************************/
/// <summary>
/// Align ghost object to surface based on raycast hit normal
/// </summary>使得 地板 layer 层级 的物体 ,创建的 时候 可以 避免相互之间 重叠
/// <param name="itemToAlign">Item to align</param>
/// <param name="hitNormal">Normal to use to align object</param>
void AlignGhostToSurface(Transform itemToAlign, Vector3 hitNormal)
{
if (itemToAlign == null) return;
itemToAlign.rotation = Quaternion.FromToRotation(Vector3.up, hitNormal) * Quaternion.Euler(new Vector3(0, objectSnapCurrentRotaion, 0));
}
}
问题:
1.出现 放置物体 重叠 BUG 解决办法
参考资料:
1.
Unity3D独立游戏开发日记(二):摆放建筑物
2.
Unity 使用自定义资源(.asset)配置数据
3.