https://blog.csdn.net/wfenglinxu/article/details/46732147 游戏中的数学与物理(一)
1.1让物体沿水平方向运动
了解匀速直线运动(最简单的运动),若左边为x,速度为v,则:x+=v,v=-v。
注意: 注意物体的中心点,不同的游戏引擎,可能设计的中心点不同,而中心点不同,边界也就不同。
1.2通过键盘控制物体的运动
主要用到小学学的勾股定理,原理很简单,而且很实用。
PS: 在模拟运动时,要控制个方向的速度。
随机喷射:
正态分布:
可以根据均匀分布的随机数生成满足正态分布的随机数的算法。
Box-Muller 算法隐含的原理非常深奥,但结果却是相当简单。它一般是要得到服从正态分布的随机数,基本思想是先得到服从均匀分布的随机数后再将服从均匀分布的随机数转变为服从正态分布。
偏态分布:
第三章 碰撞检测
德摩根定律:对全体条件的否定可以分解为对所有单个条件的否定。
矩形之间的碰撞检测:分别对两个矩形的上坐标和下坐标都进行判定。
应用:
用于对2D物体的判断,使用屏幕空间的坐标
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 任意形状,任意长宽的物体相交时弹开
/// 在相交时,沿着向量方向移动,可以是任意现状的物体相交,任意大小的物体相交,UI等
/// </summary>
public class OpenDrawPlane : MonoBehaviour
{
public List<Transform> listTrans = new List<Transform>();
public List<Info> listScreenPos= new List<Info>();
public float prinfWidth = 150;
public float prinfHeight = 160;
/// <summary>
/// 因为这个画板的中心点还需要加上地下画笔的高度
/// </summary>
[Range(-40, 40)]
public float heightScreen=20;
/// <summary>
/// 调整偏移的比率
/// </summary>
[Range(-20, 20)]
public float offsetX =0;
[Range(-40, 40)]
public float offsetY = 0;
Vector3 drawSheepPanelFar = new Vector3(0, -100, -870);
void Update()
{
listScreenPos.Clear();
//适应不同分辨率的画板长宽
prinfWidth = Screen.width / (Screen.width / prinfWidth);
prinfHeight = Screen.height / (Screen.height / prinfHeight);
heightScreen = Screen.height / (Screen.height / heightScreen);
//for (int row = 0; row < listTrans.Count; row++)
//{
// Vector3 screenPos =
// Camera.main.WorldToScreenPoint(listTrans[row].transform.position);
// Info info = new Info();
// info.trans = listTrans[row].transform;
// info.screenPos = screenPos;
// info.width = 100;
// info.height = 100;
// listScreenPos.Add(info);
//}
Transform drawBoardPar = transform;
for (int row = 0; row < drawBoardPar.GetChildCount(); row++)
{
Transform trans = drawBoardPar.GetChild(row);
if (trans.localPosition != drawSheepPanelFar)
{
Vector3 screenPos = Camera.main.WorldToScreenPoint(trans.position);
Info info = new Info();
info.trans = trans;
info.screenPos = screenPos;
//此处可以是不同的长宽度物体,或者直接获取物件相在屏幕空间下的长宽
info.width = prinfWidth;
info.height = prinfHeight;
listScreenPos.Add(info);
}
}
for (int row = 0; row < listScreenPos.Count; row++)
{
for (int cow = 0; cow < listScreenPos.Count; cow++)
{
//画板和画板的检测
if (listScreenPos[row].trans != listScreenPos[cow].trans)
{
//在相交时,沿着向量方向移动,可以是任意现状的物体相交,任意大小的物体相交
//两个物体的位置的x位置和两物体的y的位置 x<width*2 y<height*2
float centerX = Mathf.Abs(listScreenPos[row].screenPos.x - listScreenPos[cow].screenPos.x);
float centerY = Mathf.Abs(listScreenPos[row].screenPos.y - listScreenPos[cow].screenPos.y);
if (centerX < (listScreenPos[row].width / 2 + listScreenPos[cow].width / 2) &&
centerY < (listScreenPos[row].height / 2 + listScreenPos[cow].height / 2))
{
//Debug.Log(listScreenPos[row].trans.gameObject.name + " 和 " + listScreenPos[cow].trans.gameObject.name + " 想交");
/对于中心点完全相同的,则dir =0,则需要减去一个位置偏移-new Vec(0.2,0.2f,0.2))
Vector3 dir = listScreenPos[row].trans.localPosition - listScreenPos[cow].trans.localPosition;
//设置在屏幕以内运动
Vector3 screenPosRow = Camera.main.WorldToScreenPoint(listScreenPos[row].trans.position);
if ((screenPosRow.x + listScreenPos[row].width / 2) < Screen.width && (screenPosRow.x - listScreenPos[row].width / 2) > 0 &&
(screenPosRow.y + listScreenPos[row].height / 2) < Screen.height && (screenPosRow.y - listScreenPos[row].height / 2) > 0 + heightScreen)
{
listScreenPos[row].trans.Translate(dir.normalized * Time.deltaTime);
}
}
}
else
{
//画板和屏幕的检测
Vector3 screenPosRow = Camera.main.WorldToScreenPoint(listScreenPos[row].trans.position);
if ((screenPosRow.y + listScreenPos[row].height / 2) > Screen.height)
{
listScreenPos[row].trans.Translate(-listScreenPos[row].trans.up * Time.deltaTime);
}
else if ((screenPosRow.y - listScreenPos[row].height / 2) < 0 + heightScreen)
{
listScreenPos[row].trans.Translate(listScreenPos[row].trans.up * Time.deltaTime);
}
if ((screenPosRow.x + listScreenPos[row].width / 2) > Screen.width)
{
listScreenPos[row].trans.Translate(-listScreenPos[row].trans.right * Time.deltaTime);
}
else if ((screenPosRow.x - listScreenPos[row].width / 2) < 0)
{
listScreenPos[row].trans.Translate(listScreenPos[row].trans.right * Time.deltaTime);
}
}
}
}
}
public class Info
{
public Transform trans;
public Vector3 screenPos;
public float width = 150;
public float height = 160;
}
}
用于对3D物体的判断,加入z向量,使用世界空间的坐标
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour {
public List<Transform> listTrans = new List<Transform>();
public List<Info> listScreenPos = new List<Info>();
void Update()
{
listScreenPos.Clear();
for (int row = 0; row < listTrans.Count; row++)
{
Info info = new Info();
info.trans = listTrans[row].transform;
info.screenPos = listTrans[row].transform.position;
info.width = 1.2f; //单位为1的cube
info.height = 1.2f;
info.back = 1.2f;
listScreenPos.Add(info);
}
for (int row = 0; row < listScreenPos.Count; row++)
{
for (int cow = 0; cow < listScreenPos.Count; cow++)
{
//画板和画板的检测
if (listScreenPos[row].trans != listScreenPos[cow].trans)
{
//在相交时,沿着向量方向移动,可以是任意现状的物体相交,任意大小的物体相交
//两个物体的位置的x位置和两物体的y的位置 x<width*2 y<height*2
float centerX = Mathf.Abs(listScreenPos[row].screenPos.x - listScreenPos[cow].screenPos.x);
float centerY = Mathf.Abs(listScreenPos[row].screenPos.y - listScreenPos[cow].screenPos.y);
float centerZ = Mathf.Abs(listScreenPos[row].screenPos.z - listScreenPos[cow].screenPos.z);
if (centerX < (listScreenPos[row].width / 2 + listScreenPos[cow].width / 2) &&
centerY < (listScreenPos[row].height / 2 + listScreenPos[cow].height / 2)&&
centerZ < (listScreenPos[row].back / 2 + listScreenPos[cow].back / 2))
{
Vector3 dir = listScreenPos[row].trans.localPosition - listScreenPos[cow].trans.localPosition;
listScreenPos[row].trans.Translate(dir.normalized * Time.deltaTime*0.2F);
}
}
}
}
}
public class Info
{
public Transform trans;
public Vector3 screenPos;
public float width = 150;
public float height = 160;
public float back = 160;
}
}
矩形攻击或 对于判断前方是否触发了NPC的判断:
角色和NPC之间的向量为DirVec
<1. 判断Vector.Forword和DirVec的点乘,大于0,在前方。
<2. 满足第一条后,在判断DirVec在Vector.Forword方向的投影,长度是否是player的可视范围。
扇形攻击:扇形攻击角度60度,半径R, |A|=|NPC->player|,但是这样检测的截面是个平面,不是弧形。
<1. 算夹角<角度/2【30】
<2. |A|<R
圆和圆的碰撞检测:对此向量求模长,后再和两者半径之和做比较
矩形和圆形物体的碰撞检测:
思路:
<1. 先把矩形长宽扩张长度r. 扩张后新的矩形包含了圆心坐标,再进行第二步
<2. 假设圆心在扩张后的四个角处,圆内有没有包含长方形最近的顶点【扩张前矩形的某个顶点】,则圆和矩形没有碰撞
细长形物体【激光\剑】与圆形物体的碰撞检测:
满足Lmin<r1+r2 ,则碰撞。
即求处Lmin的距离,把问题转换为点到线段的距离
而这个公式,是求点到一条无限长度的直线的距离,不可取。
向量表示直线上的位置向量p 【游戏编程长用向量表示直线】:
扇形物体的碰撞检测:
挥剑,受伤范围
【定比分公式】
【圆与线段的交点判断】
满足上边任意一条,则证明发生碰撞,在剑的伤害范围内。但是一般优先检测两个不可能碰撞的条件
【即(圆心到扇心的距离) > (圆半径+扇半径)】
应用:
用向量点乘法计算是否在区域内
扇形攻击:扇形攻击角度60度,半径R, |A|=|NPC->player|
<1. 算夹角<角度/2【30】
<2. |A|<R
矩形攻击区域和扇形的攻击区域怎么判断是否有交叉?【把面 的交叉转换为点的交叉】。
<1. 判断矩形的中心点是否在扇形区域内,矩形的两个顶点是否也在区域内
可视域应用:
转一个案例: https://blog.csdn.net/u014528558/article/details/85106563
public class ConeSightTest : MonoBehaviour
{
public GameObject srcPos;
public GameObject endPos;
public float Dis;
public float angle;
public Transform[] pHead;
public Transform[] pFoot;
private void Start()
{
angle = Vector3.Angle(srcPos.transform.forward, endPos.transform.position - srcPos.transform.position);
Dis = Vector3.Distance(srcPos.transform.position, endPos.transform.position);
}
void Update()
{
for (int i = 0; i < pHead.Length; i++)
{
if (IsTargetBodyInSectorRange(srcPos.transform.position, srcPos.transform.forward,
angle, Dis, pHead[i].position, pFoot[i].position))
{
Debug.DrawLine(pHead[i].position, pFoot[i].position, Color.red);
}
else
Debug.DrawLine(pHead[i].position, pFoot[i].position, Color.green);
}
}
public static bool IsTargetInSectorRange(Vector3 srcPos, Vector3 srcDir, float srcAngle, float srcDist,
Vector3 tarPos)
{
Vector3 toTarVec = tarPos - srcPos;
float deltaAngle = Vector3.Angle(srcDir, toTarVec);
if (deltaAngle < srcAngle)
{
float dist = Vector3.Distance(srcPos, tarPos);
if (dist < srcDist)
{
return true;
}
}
return false;
}
public static bool IsTargetBodyInSectorRange(Vector3 srcPos, Vector3 srcDir, float srcAngle, float srcDist,
Vector3 tarHeadPos, Vector3 tarFootPos)
{
if (IsTargetInSectorRange(srcPos, srcDir, srcAngle, srcDist, tarHeadPos))
return true;
if (IsTargetInSectorRange(srcPos, srcDir, srcAngle, srcDist, tarFootPos))
return true;
if (tarFootPos.y <= srcPos.y && srcPos.y <= tarHeadPos.y &&
IsTargetInSectorRange(srcPos, srcDir, srcAngle, srcDist, new Vector3(tarFootPos.x, srcPos.y, tarFootPos.z)))
return true;
return false;
}
}
注意:
<1. 这个工程视野界面是平行的,和unity中的相机一样,而且Dis应该还要计算一下在srcPos.transform.forward方向的投影才能作为检测距离。分别计算底部和顶部到在camera方向的投影的夹角是否<Camera-angle/2
<2. 这种方法不能检测,障碍物遮挡障碍物的情况。应该使用LOS
https://www.cnblogs.com/yangrouchuan/p/6366629.html
<3. 只检测了高度方向上的两个点的。没有检测宽度方向上的两个点