Unity3D客戶端在遊戲場景中建立阻擋並用二進位制匯出

Unity3D客戶端在遊戲場景中建立阻擋並用二進位制匯出

在公司的專案中給策劃做了一個可以在場景中建立簡單阻擋物體的小工具,也許大家有用的到的,拿出來分享一下。

整體思路是把地形用一米見方的格子分隔,然後在格子裡放置cube代替阻擋,存檔時用一維陣列,1表示阻擋,0表示沒有阻擋。如果要實現更精確的阻擋也可以直接儲存位置和旋轉資訊。本人偷懶只實現了在Game檢視中建立阻擋,想要在Scene檢視中實現如Unity自身的樹木建立系統就留給大家自己研究,哈哈,也可以留言討論啦。吐舌頭

首先上效果圖

<pre name="code" class="csharp">///<summary> 
///作用:<實現在場景中自由放置Cube,wasd移動位置,按住滑鼠右鍵旋轉鏡頭,滑鼠左鍵放置阻擋,按住左shift點左鍵刪除阻擋,滾輪也可前後移動>
///作者:易山鬆
///編寫日期:<2014-7-18>
///</summary>
using UnityEngine;
using System.Collections;
[AddComponentMenu("Scrips/CreateBlock")]
public class CameraController : MonoBehaviour
{
// 滑鼠滾輪靈敏度
public float MouseWheelSensitivity = 3f;
//鏡頭旋轉速度
public float xSpeed = 250.0f;
public float ySpeed = 120.0f;
//鏡頭移動速度
public float moveFactor = 10f;
//鏡頭旋轉角度限制,不然就轉暈了
public int yMinLimit = -20;
public int yMaxLimit = 80;
private float x = 0.0f;
private float y = 0.0f;
//右鍵是否按下標誌
private bool mousedown;
void Start()
{
Vector3 angles = transform.eulerAngles;
x = angles.y;
y = angles.x;
// Make the rigid body not change rotation  
if (rigidbody)
{
rigidbody.freezeRotation = true;
}
}
// Update is called once per frame  
void Update()
{
Move();
CreateCube();
}
void LateUpdate()
{
if (Input.GetButtonDown("Fire2"))//檢測是否按下滑鼠右鍵
{
mousedown = true;
}
if (Input.GetButtonUp("Fire2"))//判斷當滑鼠右鍵鬆開  
{
mousedown = false;
}
if (mousedown) //實測如果直接判斷右鍵按下會卡頓,原因還沒了解,希望知道的人能告訴我
{
x  = Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
y = ClampAngle(y, yMinLimit, yMaxLimit);
Quaternion rotation = Quaternion.Euler(y, x, 0);
transform.rotation = rotation;
}
}
//控制鏡頭移動
void Move()
{
if (Input.GetKey("w"))
{
transform.Translate(Vector3.forward * Time.deltaTime * moveFactor);
}
if (Input.GetKey("s"))
{
transform.Translate(Vector3.back * Time.deltaTime * moveFactor);
}
if (Input.GetKey("a"))
{
transform.Translate(Vector3.left * Time.deltaTime * moveFactor);
}
if (Input.GetKey("d"))
{
transform.Translate(Vector3.right * Time.deltaTime * moveFactor);
}
}
//控制有關建立阻擋的操作
void CreateCube()
{
//如果left shift按下就可以刪除阻擋了
if (Input.GetKey(KeyCode.LeftShift))
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.name == "Cube")
{
Destroy(hit.transform.gameObject);
}
}
}
}
else if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//用射線判斷滑鼠點選的位置
if (Physics.Raycast(ray, out hit))
{
Vector3 pos = hit.point;
pos.x = Mathf.FloorToInt(pos.x)   0.5f;//阻擋必須是在單元格的中間
pos.z = Mathf.FloorToInt(pos.z)   0.5f;
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.renderer.material.color = Color.magenta;
pos.y = GetHeight(pos);
cube.transform.position = pos;
cube.tag = "cube";// 設定標籤便於後面匯出阻擋時查詢,,記得在設定裡增加cube的標籤
}
}
else if (Input.GetAxis("Mouse ScrollWheel") != 0)
{
this.transform.position  = transform.forward * Input.GetAxis("Mouse ScrollWheel") * MouseWheelSensitivity;
}
}
//限制鏡頭旋轉角度
float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle  = 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
//根據地形獲取阻擋的高度,也就是y軸,思路是從很高的地方垂直往下射射線,獲得與地形的碰撞點
public static float GetHeight(Vector3 pos)
{
Ray mRay = new Ray();
Vector3 dir = Vector3.down;
if (null == Terrain.activeTerrain)
{
return pos.y;
}
mRay.direction = dir;
pos.y  = 1000;
mRay.origin = pos;
RaycastHit[] hits;
hits = Physics.RaycastAll(mRay, 2000);
if (hits.Length == 0)
return pos.y;
float maxHeight = 0.0f;
//找出碰撞點裡面的最高點
for (int index = 0; index < hits.Length; index  )
{
if (hits[index].collider.gameObject.layer == LayerMask.NameToLayer("Terrain"))//記得給layer加上Terrain
{
if (hits[index].point.y > maxHeight)
maxHeight = hits[index].point.y;
}
else
continue;
}
return maxHeight   0.1f;
}
}

  • 然後建立匯出阻擋位置資訊的指令碼,這裡我用的是二進位制格式,體積比較小。在選單Assets下找到Export即可,不需要給指令碼繫結物體。

using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using System.IO;
public class ExportObs
{
static FileStream fs;
static UInt32 mapSize;
[MenuItem("Assets/ExportObs")]//新增選單項
public static void Export()
{
if (OpenObsDataFile())
{
byte[] ObsData = GetObsDataFromCurrentScene();
if (ObsData != null)
{
WriteObsData(ObsData);
fs.Close();
}
else
{
fs.Close();
Debug.Log("There is no cube added!");
}
}
else
{
fs.Close();
Debug.Log("Can't import ObsDataFile!");
}
}
//開啟檔案
static bool OpenObsDataFile()
{
//路徑是在Assets下
string obsFilePath = Application.dataPath   @"/Obs.data";
try
{
fs = File.Open(obsFilePath, FileMode.Create);
}
catch (IOException e)
{
Debug.Log(e.Message);
return false;
}
return true;
}
static byte[] GetObsDataFromCurrentScene()
{
byte[] temp = null;
//獲取遊戲中的Cube
GameObject[] gos = GameObject.FindGameObjectsWithTag("cube");
if (0 != gos.Length)
{
var terrainData = Terrain.activeTerrain.terrainData;
mapSize = Convert.ToUInt32(terrainData.size.x * terrainData.size.z);
temp = new byte[mapSize];
for (int i = 0; i < mapSize; i  )
temp[i] = 0;
foreach (GameObject go in gos)
{
//根據位置座標算出陣列中的索引
var pos = go.transform.position;
var x = Mathf.FloorToInt(pos.x);
var z = (int)(Mathf.FloorToInt(pos.z) * terrainData.size.x);
temp[x   z] = 1;
}
}
return temp;
}
static void WriteObsData(byte[] obsData)
{
BinaryWriter bw = new BinaryWriter(fs);
//首先寫入地圖大小,為簡單起見,假定地圖是正方形的,所以只需寫入一邊的長
bw.Write(Convert.ToUInt16(Terrain.activeTerrain.terrainData.size.x));
for (int i = 0; i < mapSize; i  )
{
bw.Write(obsData[i]);
}
bw.Flush();
bw.Close();
Debug.Log("Export success!");
}
}
  • 最後是匯入之前的阻擋,這裡可以再Scene檢視下操作

using UnityEngine;
using System.Collections;
using System.IO;
using System;
using UnityEditor;
public class ImportObs
{
static FileStream fs;
[MenuItem("Assets/ImportObs")]
static void Import ()
{
if (OpenObsDataFile ()) {
ReadObsData ();
fs.Close ();
}
}
public static bool OpenObsDataFile ()
{
string obsFilePath = Application.dataPath   @"/Obs.data";
try {
fs = File.Open (obsFilePath, FileMode.Open);
} catch (IOException e) {
Debug.Log (e.Message);
return false;
}
return true;
}
static void ReadObsData ()
{
BinaryReader br = new BinaryReader (fs);
UInt16 size = br.ReadUInt16 ();
Vector3 tempPos = Vector3.one;
for (UInt16 i = 0; i < size; i  )
for (UInt16 j = 0; j < size; j  ) {
if (br.ReadBoolean ()) {
tempPos.z = i;
tempPos.x = j;
tempPos.y = CameraController.GetHeight (tempPos);
tempPos.z  = 0.5f;
tempPos.x  = 0.5f;
DrawObs (tempPos);
}
}
}
static void DrawObs (Vector3 pos)
{
GameObject cube = GameObject.CreatePrimitive (PrimitiveType.Cube);
cube.transform.position = pos;
}
}

好了,到這裡已經完成了一個簡單的阻擋建立工具,用在簡單的場景下是足夠了。歡迎各位拍磚!大笑