Unity3D客户端在游戏场景中创建阻挡并用二进制导出

作者:ys496332598

在公司的项目中给策划做了一个可以在场景中创建简单阻挡物体的小工具,也许大家有用的到的,拿出来分享一下。

整体思路是把地形用一米见方的格子分隔,然后在格子里放置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;

		}

}

好了,到这里已经完成了一个简单的阻挡创建工具,用在简单的场景下是足够了。欢迎各位拍砖!大笑



发表评论

0个评论

我要留言×

技术领域:

我要留言×

留言成功,我们将在审核后加至投票列表中!

提示x

Unity3D知识库已成功保存至我的图谱现在你可以用它来管理自己的知识内容了

删除图谱提示×

你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?

删除节点提示×

无法删除该知识节点,因该节点下仍保存有相关知识内容!

删除节点提示×

你确定要删除该知识节点吗?