使用分离轴定理找到 MTV(最小平移向量)
Finding the MTV (Minimal Translation Vector) using Separating Axis Theorem
所以我一直在尝试使用分离轴定理在我的游戏项目中进行碰撞检测和响应。我已经设法检测到碰撞,但对于我的一生,我无法弄清楚如何应对它。我试图找到最小平移矢量,但我不确定如何将其变成实际矢量,以便我可以计算如何响应碰撞。我一个接一个地阅读了教程,并且在这里查看了许多以前提出的问题,但我无法弄清楚如何实现它。我真的很难理解如何找到和使用 MTV,所以如果有人真的能帮助我理解,也许给我一些例子,这样我就可以在实施中理解它,而不仅仅是理论,那将是很多感谢,对于给您带来的不便,我深表歉意。我的代码确实成功检测到碰撞,下面是它:
//NOTE: This is within the HitPolygon class, so "this" is referencing itself as a polygon
public Projection project(Vector2D axis){
float min = axis.dot(this.getVertices().get(0));
float max = min;
Vector2D vecMax = new Vector2D(0, 0), vecMin = new Vector2D(0, 0);
for(int i = 1; i < this.getVertices().size(); i++){
float p = axis.dot(this.getVertices().get(i));
if(p < min){
min = p;
vecMin = this.getVertices().get(i);
}
if(p > max){
max = p;
vecMax = this.getVertices().get(i);
}
}
Projection result = new Projection(min, max, vecMin, vecMax, axis);
return result;
}
public boolean contains(Projection p1, Projection p2){
return(p1.min >= p2.min && p1.min <= p2.max) || (p1.max >= p2.min && p1.max <= p2.max);
}
public boolean overlap(Projection a, Projection b){
if(contains(a, b)) return true;
if(contains(b, a)) return true;
return false;
}
public boolean collide(HitPolygon b){
ArrayList<Vector2D> axes1 = this.getAxes(0);
ArrayList<Vector2D> axes2 = b.getAxes(0);
for(int i = 0; i < axes1.size(); i++){
Vector2D axis = axes1.get(i);
Projection p1 = this.project(axis), p2 = b.project(axis);
if(!overlap(p1, p2)) return false;
else{
float start = p1.min > p2.min ? p1.min : p2.min;
float end = p1.max < p2.max ? p1.max : p2.max;
float translation = end - start;
//translation might be the MTV, more or less, but it's not a vector and I don't know how to turn it into one.
}
}
for(int i = 0; i < axes2.size(); i++){
Vector2D axis = axes2.get(i);
Projection p1 = this.project(axis), p2 = b.project(axis);
if(!overlap(p2, p1)) return false;
else{
}
}
System.out.println("collide");
return true;
}
这是简单的投影 class:
class Projection{
public Vector2D minVec, maxVec;
public float min, max;
public Vector2D axis = new Vector2D(0, 0);
public Projection(float min, float max, Vector2D minVec, Vector2D maxVec, Vector2D axis){
this.min = min;
this.max = max;
this.minVec = minVec;
this.maxVec = maxVec;
this.axis = axis;
}
public float minDot(){
return axis.dot(minVec);
}
public float maxDot(){
return axis.dot(maxVec);
}
}
如果需要任何额外的代码,我很乐意提供。请原谅我不了解如何找到和使用 MTV,但非常感谢任何帮助。谢谢!
其实很简单。您需要知道您使用的轴的角度以及 2 个对象在每种情况下碰撞的深度(深度是预计碰撞区域的长度)。
使用以下技术将每个数据转换为向量:
var vecX = Math.cos(axisAngle) * deepness;
var vecY = Math.sin(axisAngle) * deepness;
然后,将向量加在一起:
var sumX = vec1X + vec2X + ...;
var sumY = vec1Y + vec2Y + ...;
在这之后你会得到一个和向量,这是你需要使用的位移向量(因为魔法)
所以我一直在尝试使用分离轴定理在我的游戏项目中进行碰撞检测和响应。我已经设法检测到碰撞,但对于我的一生,我无法弄清楚如何应对它。我试图找到最小平移矢量,但我不确定如何将其变成实际矢量,以便我可以计算如何响应碰撞。我一个接一个地阅读了教程,并且在这里查看了许多以前提出的问题,但我无法弄清楚如何实现它。我真的很难理解如何找到和使用 MTV,所以如果有人真的能帮助我理解,也许给我一些例子,这样我就可以在实施中理解它,而不仅仅是理论,那将是很多感谢,对于给您带来的不便,我深表歉意。我的代码确实成功检测到碰撞,下面是它:
//NOTE: This is within the HitPolygon class, so "this" is referencing itself as a polygon
public Projection project(Vector2D axis){
float min = axis.dot(this.getVertices().get(0));
float max = min;
Vector2D vecMax = new Vector2D(0, 0), vecMin = new Vector2D(0, 0);
for(int i = 1; i < this.getVertices().size(); i++){
float p = axis.dot(this.getVertices().get(i));
if(p < min){
min = p;
vecMin = this.getVertices().get(i);
}
if(p > max){
max = p;
vecMax = this.getVertices().get(i);
}
}
Projection result = new Projection(min, max, vecMin, vecMax, axis);
return result;
}
public boolean contains(Projection p1, Projection p2){
return(p1.min >= p2.min && p1.min <= p2.max) || (p1.max >= p2.min && p1.max <= p2.max);
}
public boolean overlap(Projection a, Projection b){
if(contains(a, b)) return true;
if(contains(b, a)) return true;
return false;
}
public boolean collide(HitPolygon b){
ArrayList<Vector2D> axes1 = this.getAxes(0);
ArrayList<Vector2D> axes2 = b.getAxes(0);
for(int i = 0; i < axes1.size(); i++){
Vector2D axis = axes1.get(i);
Projection p1 = this.project(axis), p2 = b.project(axis);
if(!overlap(p1, p2)) return false;
else{
float start = p1.min > p2.min ? p1.min : p2.min;
float end = p1.max < p2.max ? p1.max : p2.max;
float translation = end - start;
//translation might be the MTV, more or less, but it's not a vector and I don't know how to turn it into one.
}
}
for(int i = 0; i < axes2.size(); i++){
Vector2D axis = axes2.get(i);
Projection p1 = this.project(axis), p2 = b.project(axis);
if(!overlap(p2, p1)) return false;
else{
}
}
System.out.println("collide");
return true;
}
这是简单的投影 class:
class Projection{
public Vector2D minVec, maxVec;
public float min, max;
public Vector2D axis = new Vector2D(0, 0);
public Projection(float min, float max, Vector2D minVec, Vector2D maxVec, Vector2D axis){
this.min = min;
this.max = max;
this.minVec = minVec;
this.maxVec = maxVec;
this.axis = axis;
}
public float minDot(){
return axis.dot(minVec);
}
public float maxDot(){
return axis.dot(maxVec);
}
}
如果需要任何额外的代码,我很乐意提供。请原谅我不了解如何找到和使用 MTV,但非常感谢任何帮助。谢谢!
其实很简单。您需要知道您使用的轴的角度以及 2 个对象在每种情况下碰撞的深度(深度是预计碰撞区域的长度)。
使用以下技术将每个数据转换为向量:
var vecX = Math.cos(axisAngle) * deepness;
var vecY = Math.sin(axisAngle) * deepness;
然后,将向量加在一起:
var sumX = vec1X + vec2X + ...;
var sumY = vec1Y + vec2Y + ...;
在这之后你会得到一个和向量,这是你需要使用的位移向量(因为魔法)