Unity简单地图循环长箭头实现

作者: 玄猫大人 | 来源:发表于2017-05-17 20:02 被阅读379次

    最近在做大地图,行军导航过程中会试用到导向箭头,这里整理下实现的过程。

    最终效果

    箭头实例图

    思路分解

    1 地图上会有很多这种指向的箭头,同时长度是不固定的,需要根据不同的起始点和终点进行计算角度和方向
    2 长度在Unity中可以通过scale进行控制
    3 需要重复的箭头,要实现图片平铺的效果,这里就有一个Tiling的概念
    4 如果需要移动,可以使用材质的Offset属性
    5 选择角度计算过程中,需要考虑到四个象限的不同情况
    6 箭头显示要在最上层,不能被地表的物体遮挡

    看到这些思路你们能实现的出来吗?不卖官司,接着往下看。

    具体实现

    原料:

    Plane一只,做2D的时候用Sprite原理也是一样的;
    箭头图片一张;
    Shader文件一个;
    材质球一枚;
    CS文件一个;

    操作步骤:

    1 先处理材质球,新建一个Shader,命名为TransparentOverlay,具体代码如下:

    Shader "Custom/TransparentOverlay"  
    {  
        Properties   
        {  
            _MainTex ("MainTex(RGBA)", 2D) = "white" {}  
        }
    
        SubShader   
        {  
            LOD 100
            Cull Off Lighting Off ZWrite Off ZTest Always
            Blend SrcAlpha OneMinusSrcAlpha
            
            Tags
            {
                "Queue" = "Overlay"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
            }
    
            Pass
            {
                SetTexture[_MainTex]
            }
        } 
    }
    

    2 利用该Shader创建一个材质球,将箭头图片赋给材质球

    箭头和对应的材质球

    3 在场景中新建一个Plane,将材质球赋个Plane,调整Plane到你需要的尺寸,做成预制件

    我所用的参数,根据场景自行修改

    4 新建一个C#脚本,代码有点长,直接看注释咯,需要额外说的是,计算长度和角度公式大致如下,在其他地方使用的时候还是要根据当前场景的相机,地图大小等等进行相应的微调。

    using UnityEngine;
    
    /// <summary>
    /// 行军路线
    /// </summary>
    public class MapLine : MonoBehaviour {
        private Vector3 startPos = Vector3.one;
        private Vector3 endPos = Vector3.one;
        private Vector3 rotation = Vector3.zero;
    
        private Material material;
        private bool isMove;
        private Vector2 moveDir;
        private Vector2 resetOffset;
    
        void Awake()
        {
            isMove = false;
            moveDir = new Vector2(0, 0.01f);
            resetOffset = new Vector2(0, 100);
            material = GetComponent<Renderer>().material;
        }
        
        /// <summary>
        /// 设置材质的Offset的属性,让箭头移动起来
        /// </summary>
        private void Update()
        {
            if(isMove)
            {
                if (material.mainTextureOffset == resetOffset)
                    material.mainTextureOffset = moveDir;
                material.mainTextureOffset += moveDir;
            }
        }
    
        public void SetLine(Vector3 startPos, Vector3 endPos)
        {
            this.startPos = startPos;
            this.endPos = endPos;
            transform.localScale = Vector3.one * 0.05f;
            transform.position = startPos;
            transform.eulerAngles = Vector3.zero;
    
            var scale = transform.localScale;
            var lineLong = CalLineLong() * 2;
            scale.z = scale.z * lineLong;
            transform.localScale = scale;
            rotation.y = CalLineAngle();
            transform.eulerAngles = rotation;
            material.mainTextureScale = new Vector2(1, lineLong);
            transform.Translate(0,0,lineLong / 4, Space.Self);
            isMove = true;
        }
    
        /// <summary>
        /// 计算行军路线长度
        /// </summary>
        private float CalLineLong()
        {
            return Mathf.Sqrt(Mathf.Pow(startPos.x - endPos.x, 2) + Mathf.Pow(startPos.z - endPos.z, 2));
        }
    
        /// <summary>
        /// 计算行军路线角度
        /// </summary>
        private float CalLineAngle()
        {
            //斜边长度
            float length = Mathf.Sqrt(Mathf.Pow((startPos.x - endPos.x), 2) + Mathf.Pow((startPos.z - endPos.z), 2));
            //对边比斜边 sin
            float hudu = Mathf.Asin(Mathf.Abs(startPos.z - endPos.z) / length);
            float ag = hudu * 180 / Mathf.PI;
    
            //第一象限
            if ((endPos.x - startPos.x) >= 0 && (endPos.z - startPos.z >= 0))
                ag = -ag + 90;
            //第二象限
            else if ((endPos.x - startPos.x) <= 0 && (endPos.z - startPos.z >= 0))
                ag = ag - 90;
            //第三象限
            else if ((endPos.x - startPos.x) <= 0 && (endPos.z - startPos.z) <= 0)
                ag = -ag +270;
            //第四象限
            else if ((endPos.x - startPos.x) >= 0 && (endPos.z - startPos.z) <= 0)
                ag = ag + 90;
            return ag;
        }
    }
    
    

    5 将脚本附加到预制件中,通过设置SetLine方法设置起点和终点就可以在地图上画箭头了。

    PS:写代码还是要学好数学呀,不做都不知道象限是个啥,顺路复习初中数学

    相关文章

      网友评论

      本文标题:Unity简单地图循环长箭头实现

      本文链接:https://www.haomeiwen.com/subject/wboxxxtx.html