Unity 面试篇|(八)Unity机试篇 【全面总结 | 持续更新】

目录

    • 1.假设当前市场价一只鸡10元,一只鸭12元5角。请写一个函数ShowPrice,输入参数分别为鸡和鸭的个数(非负整型),功能为显示出总价钱,精确到分。例如调用ShowPrice(5,10)后输出175.00。请注意程序的可读性和易于维护性。
    • 2.判断点与线的位置关系
    • 3.计算点在直线上的投影(向量投影)
    • 4.判断多边形是否为凸多边形
    • 5.判断线段与线段是否共线
    • 6.判断线段与线段是否重合(非相交)
    • 7.线段与线段是否相交
    • 8.计算直线与直线的交点
    • 9.射线与线段是否相交,以及交点
    • 10.点围绕另一点旋转指定角度
    • 11.点是否在任意多变内
    • 12.写一个计时器工具,从整点开始计时,格式为:00:00:00
    • 13.用鼠标实现在场景中拖动物体,用鼠标滚轮实现缩放
    • 14.写一个角色控制器,鼠标控制屏幕晃动,鼠标控制开枪。
    • 15.3D空间有三个cube当做点,有一条鱼的模型,要求在三点之间游动,要
    • 16.设计一款计时器(Timer)功能,拥有基础事件:开始计时、暂停计时,停止计时【计时器事件】

1.假设当前市场价一只鸡10元,一只鸭12元5角。请写一个函数ShowPrice,输入参数分别为鸡和鸭的个数(非负整型),功能为显示出总价钱,精确到分。例如调用ShowPrice(5,10)后输出175.00。请注意程序的可读性和易于维护性。

static void ShowPrice(int num_chicken, int num_duck) {
float totalPrice = 0.00f;
float price_chicken = 10f;
float price_duck = 12.5f;
totalPrice = num_chicken * price_chicken + num_duck * price_duck;
Console.WriteLine(“总价钱为:{0:0.00}”, totalPrice);
}

2.判断点与线的位置关系

已知点P(x,y),与直线上A(x1,y1),B(x2,y2)两点,通过向量AP与BP的叉乘返回的结果,即可确定点在直线的位置关系。
判断依据:1)等于0:点在直线上;2)小于0:点在直线的左侧;3)大于0:点在直线的右侧

 /// 
 /// 2D叉乘
 /// 
 public static float CrossProduct2D(Vector2 v1, Vector2 v2)
 {
     //叉乘运算公式 x1*y2 - x2*y1
     return v1.x * v2.y - v2.x * v1.y;
 }
 /// 
 /// 点与线的位置关系
 /// 
 /// ==0:点在线上 <0:点在线的左侧 >0:点在线的右侧
 public static int IsPointToLinePosition(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
 {
     float crossValue = CrossProduct2D(point - lineStart, lineEnd - lineStart);
     if (crossValue < 0) return -1;
     if (crossValue > 0) return 1;
     return 0;
 }

3.计算点在直线上的投影(向量投影)

已知点P(x,y),与直线上两点A(x1,y1),B(x2,2),求P点在直线AB上的投影
知识点:通过向量投影公式(点乘),即可获得点P在直线AB投影点。

    /// 
    /// 2D叉乘
    /// 
    public static float CrossProduct2D(Vector2 v1, Vector2 v2)
    {
        //叉乘运算公式 x1*y2 - x2*y1
        return v1.x * v2.y - v2.x * v1.y;
    }
    /// 
    /// 点是否在直线上
    /// 
    public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
        return Mathf.Abs(value) < 0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
    }
    /// 
    /// 点到直线上的投影坐标
    /// 
    public static Vector2 Point2LineProject(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
    {
        if (IsPointOnLine(point, lineStart, lineEnd))
            return point;
        Vector2 v = point - lineStart;
        Vector2 u = lineEnd - lineStart;
        //求出长度
        float u1Length = Vector2.Dot(u, v) / u.magnitude;
        return u1Length * u.normalized + lineStart;
    }

4.判断多边形是否为凸多边形

已知多边形的逆时针顶点序列,依次判断相邻两个线段走向是否一致即可。
(点与线段位置关系,因为规定为逆时针顶点序列,所以只要两个相邻线段的叉乘都小于0则该多边形为凸多边形)

   /// 
   /// 2D叉乘
   /// 
   public static float CrossProduct2D(Vector2 v1, Vector2 v2)
   {
       //叉乘运算公式 x1*y2 - x2*y1
       return v1.x * v2.y - v2.x * v1.y;
   }
   /// 
   /// 点与线的位置关系
   /// 
   /// ==0:点在线上 <0:点在线的左侧 >0:点在线的右侧
   public static int IsPointToLinePosition(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
   {
       float crossValue = CrossProduct2D(point - lineStart, lineEnd - lineStart);
       if (crossValue < 0) return -1;
       if (crossValue > 0) return 1;
       return 0;
   }
   /// 
   /// 是否为凸多边形
   /// 
   /// 逆时针点序列
   public static bool IsConvexPolygon(List<Vector2> points)
   {
       //计算每个顶点的转向,如果有不一致的转向,则表示该多边形不是凸多边形
       if (points.Count < 3) return false;
       bool isConvex = true;
       for (int i = 1; i < points.Count; i++)
       {
           Vector2 point = points[i];
           //上一个点
           Vector2 point1 = points[i - 1];
           //下一个点,如果超出当前点集合,则需要获取第一个点
           int nextIndex = i + 1;
           if (nextIndex >= points.Count) nextIndex = 0;
           Vector2 point2 = points[nextIndex];
           //计算朝向,因为点集合为逆时针点序列,如果点在线段右侧,则表示该角大于180 该多边形为凹多边形
           float value = IsPointToLinePosition(point1, point, point2);
           if (value > 0)
           {
               isConvex = false;
               break;
           }
       }
       return isConvex;
   }

5.判断线段与线段是否共线

思路:
1)先判断两条线段是否平行,即两条线段的叉积等于0
2)在判断两条线段是否共线,即线段1一个点在线段2的延长线上

  /// 
  /// 线段与线段是否共线
  /// 
  public static bool IsSegmentCollineation(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
                  Vector2 segment2End)
  {
      //1.判断两个向量是否平行
      float value = CrossProduct2D(segment1End - segment1Start, segment2End - segment2Start);
      if (Mathf.Abs(value) < 0.0003f)
      {
          // 平行,则判断一个线上的点是否在另一线上
          if (IsPointOnLine(segment2Start, segment2End, segment2Start))
              return true;
      }
      return false;
  }

6.判断线段与线段是否重合(非相交)

1)判断两条线段必须共线
2)然后片段判断线段A的起终点是否在线段B,以及线段B的起终点,是否在线段A上,只要有一个条件成立,则可以认为两条线段是重合的

1)判断两条线段是否共线
2)对两条线段的4个定点进行排序,如果1,3 或 1,4 为为同一条线段上的点,则可以任务两条线段是否重合的

 /// 
 /// 线段与线段是否重合(全部重合或局部重合)
 /// 
 public static bool IsSegmentCoincide(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
                 Vector2 segment2End)
 {
     //先判断两条线段是否在同一条线上
     if (!IsSegmentCollineation(segment1Start, segment1End, segment2Start, segment2End))
         return false;
     //如果是相同的起终点
     if (segment1Start == segment2Start && segment1End == segment2End) return true;
     //判断检测点是否在另一条线段上
     if (IsPointOnSegment2(segment1Start, segment2Start, segment2End) ||
         IsPointOnSegment2(segment1End, segment2Start, segment2End) ||
         IsPointOnSegment2(segment2Start, segment1Start, segment1End) ||
         IsPointOnSegment2(segment2End, segment1Start, segment1End))
         return true;
     return false;
 }

7.线段与线段是否相交

    /// 
    /// 线段是否相交
    /// 
    public static bool IsSegmentIntersect(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
                    Vector2 segment2End)
    {
        //快速排斥实验
        if (Mathf.Min(segment1Start.x, segment1End.x) <= Mathf.Max(segment2Start.x, segment2End.x)
            && Mathf.Min(segment2Start.x, segment2End.x) <= Mathf.Max(segment2Start.x, segment2End.x)
            && Mathf.Min(segment1Start.y, segment1End.y) <= Mathf.Max(segment2Start.y, segment2End.y)
            && Mathf.Min(segment2Start.y, segment2End.y) <= Mathf.Max(segment1Start.y, segment1End.y))
        {
            //先判断线段是否重合,重合,则认为也是相交
            if (IsSegmentCoincide(segment1Start, segment1End, segment2Start, segment2End))
                return true;
            //互为跨立的判断 一线的点相对另一线的位置关系,左右 -1 1,之和为 0
            int state = IsPointToLinePosition(segment1Start, segment2Start, segment2End) +
                        IsPointToLinePosition(segment1End, segment2Start, segment2End) +
                        IsPointToLinePosition(segment2Start, segment1Start, segment1End) +
                        IsPointToLinePosition(segment2End, segment1Start, segment1End);
            if (state == 0) return true;
        }
        return false;
    }

8.计算直线与直线的交点

   /// 
   /// 求直线的交点
   /// 
   public static Vector2 LineIntersectPoint(Vector2 line1Start, Vector2 line1End, Vector2 line2Start,
                   Vector2 line2End)
   {
       //两点式公式
       //x0 = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
       //y0 = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
       float x1 = line1Start.x, x2 = line1End.x, x3 = line2Start.x, x4 = line2End.x;
       float y1 = line1Start.y, y2 = line1End.y, y3 = line2Start.y, y4 = line2End.y;
       Vector2 point = Vector2.zero;
       point.x = ((x3 - x4) * (x2 * y1 - x1 * y2) - (x1 - x2) * (x4 * y3 - x3 * y4)) / ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4));
       point.y = ((y3 - y4) * (y2 * x1 - y1 * x2) - (y1 - y2) * (y4 * x3 - y3 * x4)) / ((y3 - y4) * (x1 - x2) - (y1 - y2) * (x3 - x4));
       return point;
   }

9.射线与线段是否相交,以及交点

  • 所在直线是否相交
  • 点是否在射线以及直线上
   /// 
   /// 射线与线段是否相交
   /// 
   public static bool IsRaySegmentIntersect(Vector2 rayStart, Vector2 rayDir, Vector2 segmentStart, Vector2 segmentEnd, out Vector2 point)
   {
       //先计算两条直线是否相交
       if (!LineIntersectPoint(rayStart, rayStart + rayDir * 1, segmentStart, segmentEnd, out point))
           return false;
       //判断交点的位置是否在射线上 方向相同可以确定点在射线上
       if (Vector2.Dot((point - rayStart).normalized, rayDir.normalized) < 0) return false;
       //点是否在线段上
       if (!IsPointOnSegment2(point, segmentStart, segmentEnd)) return false;
       return true;
   }

10.点围绕另一点旋转指定角度

   /// 
   /// 点绕另一个点进行旋转
   /// 
   public static Vector2 PointRoationOnPoint(Vector2 originPoint, Vector2 point, float angle)
   {
       if (originPoint == point) return originPoint;
       Vector2 resultPoint = Vector2.zero;
       Vector2 v = (point - originPoint).normalized;
       angle *= Mathf.Deg2Rad;
       resultPoint.x = v.x * Mathf.Cos(angle) - v.y * Mathf.Sin(angle);
       resultPoint.y = v.x * Mathf.Sin(angle) + v.y * Mathf.Cos(angle);
       return resultPoint * Vector2.Distance(originPoint, point) + originPoint;
   }
   /// 
   /// 点集合绕点旋转
   /// 
   public static List<Vector2> PointsRoationOnPoint(List<Vector2> points, Vector2 point, float angle)
   {
       List<Vector2> resultPoints = new List<Vector2>();
       if (points == null) return resultPoints;
       for (int i = 0; i < points.Count; i++)
       {
           Vector2 nPoint = PointRoationOnPoint(point, points[i], angle);
           resultPoints.Add(nPoint);
       }
       return resultPoints;
   }

11.点是否在任意多变内

  • 射线法:
    • 求出多边形所在矩形,判断点是否在矩形内
    • 点是否在多边形顶点或边上
    • 以当前点做一条水平射线,计算该射线与多边形线段的相交数量,如果为奇数则点在多边形内部
 /// 
 /// 点是否在任意多边形内部
 /// 
 /// -1:不在多边形内 0:在多边形上 1:多边形内 
 public static int IsPointInAnyPolygon(Vector2 point, List<Vector2> polygon)
 {
     //顶点数量小于3,则无法形成多边形
     if (polygon.Count < 3) return -1;
     //1.先获取多边形所在矩形范围内
     Vector2 rectMin = polygon[0], rectMax = polygon[0];
     for (int i = 1; i < polygon.Count; i++)
     {
         if (polygon[i].x < rectMin.x) rectMin.x = polygon[i].x;
         if (polygon[i].y < rectMin.y) rectMin.y = polygon[i].y;
         if (polygon[i].x > rectMax.x) rectMax.x = polygon[i].x;
         if (polygon[i].y > rectMax.y) rectMax.y = polygon[i].y;
     }
     if (point.x < rectMin.x || point.y < rectMin.y || point.x > rectMax.x || point.y > rectMax.y) return -1;
     //2.射线相交点计算
     int intersectCount = 0;
     for (int i = 0; i < polygon.Count; i++)
     {
         int nextIndex = i + 1;
         if (nextIndex >= polygon.Count)
             nextIndex = 0;
         //目标在顶点上
         if (polygon[i] == point || polygon[nextIndex] == point)
             return 0;
         //目标在线段上
         if (IsPointOnSegment2(point, polygon[i], polygon[nextIndex]))
             return 0;
         Vector2 intersectPoint;
         //射线与线段相交
         if (IsRaySegmentIntersect(point, Vector2.right, polygon[i], polygon[nextIndex], out intersectPoint))
         {
             //如果相交为线段的顶点,则需要增加2,因为在同一点进入,又在同一个点出去
             if (intersectPoint == polygon[i] || intersectPoint == polygon[nextIndex])
                 intersectCount += 2;
             else
                 intersectCount += 1;
         }
     }
     return intersectCount % 2 == 1 ? 1 : -1;
 }

12.写一个计时器工具,从整点开始计时,格式为:00:00:00

  • 挂在随便一个text物体上
  • 空格暂停与开始;小键盘0清零
   public class Timer: MonoBehaviour
   {
       private float timer = 0f;
       private int h = 0;
       private int m = 0;
       private int s = 0;
       private string timeStr = string.Empty;
       Text time_text;
       bool pause = true;
       private void Start()
       {
           time_text = gameObject.GetComponent<Text>();
       }
       void Update()
       {
           if (Input.GetKeyDown(KeyCode.Space))
           {
               pause = !pause;
           }
           timer += Time.deltaTime;
           if (!pause)
           {
               if (timer >= 1f)
               {
                   s++;
                   timer = 0;
               }
               if (s >= 60)
               {
                   m++;
                   s = 0;
               }
               if (m >= 60)
               {
                   h++;
                   m = 0;
               }
               if (h >= 99)
               {
                   h = 0;
               }
           }
           if (Input.GetKeyDown(KeyCode.Keypad0))
           {
               s = 0; m = 0; h = 0;
           }
           timeStr = string.Format("{0:D2}:{1:D2}:{2:D2}", h, m, s);
           time_text.text = timeStr;
       }
   }

13.用鼠标实现在场景中拖动物体,用鼠标滚轮实现缩放

  • 鼠标拖拽,注:被拖拽物体不能与场景内其他物体在default layer上
    public class DragObj : MonoBehaviour
    {
        private void OnMouseEnter()
        {
            gameObject.transform.localScale += new Vector3(0.1f, 0.1f, 0.1f);
        }
        private void OnMouseExit()
        {
            gameObject.transform.localScale -= new Vector3(0.1f, 0.1f, 0.1f);
        }
        private void OnMouseDrag()
        {
            Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);//从屏幕鼠标点击的位置向场景内发出一条射线
            RaycastHit floorHit;
            if (Physics.Raycast(camRay, out floorHit, 1000f, 1))//1为过滤层
            {
                transform.position = new Vector3(floorHit.point.x, floorHit.point.y + 0.25f, floorHit.point.z);
            }
        }
    }
    //滚轮缩放
    public class ViewCtrl : MonoBehaviour
    {
        public float wheelspeed;
        void Update()
        {
            transform.Translate(0, 0, Input.GetAxis("Mouse ScrollWheel") * wheelspeed * Time.deltaTime, Space.Self);
        }
    }

14.写一个角色控制器,鼠标控制屏幕晃动,鼠标控制开枪。

    public class Player : MonoBehaviour
    {
        public GameObject _prefabBullet;
        private float _angleSpeed = 120f;
        void Update()
        {
            float eularY = Input.GetAxis("Mouse X") * _angleSpeed * Time.deltaTime;
            transform.Rotate(new Vector3(0, eularY, 0));
            if (Input.GetMouseButtonDown(0))
            {
                Instantiate(_prefabBullet, transform.position, transform.rotation);
            }
        }
    }

15.3D空间有三个cube当做点,有一条鱼的模型,要求在三点之间游动,要

  • 求转向平滑一点,控制鱼的运动朝向(用四元数和欧拉角)使用transform.localRotation = Quaternion.Slerp(Quaternion a,Quaternion b,float c)实现物体平滑转向

16.设计一款计时器(Timer)功能,拥有基础事件:开始计时、暂停计时,停止计时【计时器事件】

public class Timer : MonoBehaviour
{
    public delegate void MyEventHandler(float currentTime);
    #region 计时器的三种基础事件
    public static event MyEventHandler onTimerStart;
    public static event MyEventHandler onTimerPause;
    public static event MyEventHandler onTimerStoped;
    #endregion
    private bool isStarted = false;
    public bool IsStarted
    {
        get
        {
            return isStarted;
        }
    }
    private bool isStoped = true;
    public bool IsStoped
    {
        get
        {
            return isStoped;
        }
    }
    private float totalTime = 0;
    // Update is called once per frame
    void Update()
    {
        //空格键当作“开始/暂停”键
        if (Input.GetKeyDown(KeyCode.Space))
        {
            OnChangeState();
        }
        //回车键当作“停止”键
        if (Input.GetKeyDown(KeyCode.Return))
        {
            OnSetStop();
        }
        if (isStarted)
        {
            isStoped = false;
            totalTime += Time.deltaTime;
        }
    }
    void OnChangeState()
    {
        var _startState = !isStarted;
        isStarted = _startState;
        if (isStarted)
        {
            //检查onTimerStart是否为空,防止报空 (废话了。。。下面不做赘述)
            if (onTimerStart != null)
            {
                onTimerStart(totalTime);
            }
            else
            {
                Debug.Log("onTimerStart is Empty");
            }
        }
        else
        {
            if (onTimerPause != null)
            {
                onTimerPause(totalTime);
            }
            else
            {
                Debug.Log("onTimerPause is Empty");
            }
        }
    }
    void OnSetStop()
    {
        if (onTimerStoped != null)
        {
            onTimerStoped(totalTime);
        }
        else
        {
            Debug.Log("onTimerStoped is Empty");
        }
        isStarted = false;
        isStoped = true;
        totalTime = 0;
    }
}

你可能感兴趣的:(#,2024年Unity面试题库,unity,游戏引擎,c#,算法,数据结构,面试)