目录
- 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:点在直线的右侧
/// <summary>/// 2D叉乘/// </summary>public static float CrossProduct2D(Vector2 v1, Vector2 v2){//叉乘运算公式 x1*y2 - x2*y1return v1.x * v2.y - v2.x * v1.y;}/// <summary>/// 点与线的位置关系/// </summary>/// <returns>==0:点在线上 <0:点在线的左侧 >0:点在线的右侧</returns>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投影点。
/// <summary>/// 2D叉乘/// </summary>public static float CrossProduct2D(Vector2 v1, Vector2 v2){//叉乘运算公式 x1*y2 - x2*y1return v1.x * v2.y - v2.x * v1.y;}/// <summary>/// 点是否在直线上/// </summary>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*/;}/// <summary>/// 点到直线上的投影坐标/// </summary>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则该多边形为凸多边形)
/// <summary>/// 2D叉乘/// </summary>public static float CrossProduct2D(Vector2 v1, Vector2 v2){//叉乘运算公式 x1*y2 - x2*y1return v1.x * v2.y - v2.x * v1.y;}/// <summary>/// 点与线的位置关系/// </summary>/// <returns>==0:点在线上 <0:点在线的左侧 >0:点在线的右侧</returns>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;}/// <summary>/// 是否为凸多边形/// </summary>/// <param name="points">逆时针点序列</param>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的延长线上
/// <summary>/// 线段与线段是否共线/// </summary>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 为为同一条线段上的点,则可以任务两条线段是否重合的
/// <summary>/// 线段与线段是否重合(全部重合或局部重合)/// </summary>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.线段与线段是否相交
/// <summary>/// 线段是否相交/// </summary>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,之和为 0int 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.计算直线与直线的交点
/// <summary>/// 求直线的交点/// </summary>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.射线与线段是否相交,以及交点
- 所在直线是否相交
- 点是否在射线以及直线上
/// <summary>/// 射线与线段是否相交/// </summary>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.点围绕另一点旋转指定角度
/// <summary>/// 点绕另一个点进行旋转/// </summary>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;}/// <summary>/// 点集合绕点旋转/// </summary>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.点是否在任意多变内
- 射线法:
- 求出多边形所在矩形,判断点是否在矩形内
- 点是否在多边形顶点或边上
- 以当前点做一条水平射线,计算该射线与多边形线段的相交数量,如果为奇数则点在多边形内部
/// <summary>/// 点是否在任意多边形内部/// </summary>/// <returns>-1:不在多边形内 0:在多边形上 1:多边形内 </returns>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;elseintersectCount += 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;#endregionprivate 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 framevoid 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;}
}