1.如图,△ABC和△DEF相交,假设以△ABC为参考物,我们如何把顶点D移到直线BC上,并使其看起来合理?
我们可以使△DEF沿BC的法向DG移动DG的长度,这样,就能得到我们所要的“看起来合理”
2.△ABC和△DEF的每个顶点的坐标已知,因此各边的法向量容易求出,求出后我们把各边的法向量归一化,以便
进行向量的计算。
3.现在要求DG向量。如图,DF与BC交于H点,这样,向量DC在BC边上的单位法向量的投影向量就是向量DG,而
向量DC和BC的单位法向量容易求出,于是
向量DG = 向量DC dot BC的单位法向量
DG向量求出后,使△DEF各顶点坐标加上向量DG,就求出最后结果
在程序中实现,还有一个重要的问题,在程序中如何得知哪条边被穿插(对于上图,BC边被穿插)?下面代码会告诉你如何解决这个问题。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace PolygonIntersection {
public partial class MainForm : Form
{
// Structure that stores the results of the PolygonCollision function
public struct PolygonCollisionResult
{
public bool WillIntersect; // Are the polygons going to intersect forward in time?
public bool Intersect; // Are the polygons currently intersecting
public Vector MinimumTranslationVector; // The translation to apply to polygon A to push the polygons appart.
}
// Check if polygon A is going to collide with polygon B for the given velocity
/// <summary>
/// 两多边形碰撞检测
/// 此函数必须在两多边形在当前帧并未移动时调用
/// </summary>
/// <param name="polygonA"></param>
/// <param name="polygonB"></param>
/// <param name="velocity">多边形A相对于B的速度,即A与B的合速度</param>
/// <returns></returns>
public PolygonCollisionResult PolygonCollision(Polygon polygonA, Polygon polygonB, Vector velocity)
{
PolygonCollisionResult result = new PolygonCollisionResult();
result.Intersect = true;
result.WillIntersect = true;
int edgeCountA = polygonA.Edges.Count;
int edgeCountB = polygonB.Edges.Count;
float minIntervalDistance = float.PositiveInfinity;
Vector translationAxis = new Vector();
Vector edge;
// Loop through all the edges of both polygons
//遍历两多边的所有边
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++)
{
if (edgeIndex < edgeCountA)
{
edge = polygonA.Edges[edgeIndex];
}
else
{
edge = polygonB.Edges[edgeIndex - edgeCountA];
}
// ===== 1. Find if the polygons are currently intersecting =====
// Find the axis perpendicular to the current edge
//计算当前边的单位法向量
Vector axis = new Vector(-edge.Y, edge.X);
axis.Normalize();
// Find the projection of the polygon on the current axis
//找出各顶点向量在当前单位法向量投影并返回此投影的最小和最大长度
float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
ProjectPolygon(axis, polygonA, ref minA, ref maxA);
ProjectPolygon(axis, polygonB, ref minB, ref maxB);
// Check if the polygon projections are currentlty intersecting
//检测多边形A,B在当前单位法向量的投影是否相交
if (IntervalDistance(minA, maxA, minB, maxB) > 0) result.Intersect = false;
// ===== 2. Now find if the polygons *will* intersect =====
// Project the velocity on the current axis
//计算A,B合速度在当前单位法向量的投影的长度(可正,可负)
float velocityProjection = axis.DotProduct(velocity);
// Get the projection of polygon A during the movement
if (velocityProjection < 0)
{
//速度与投影的夹角大于90度小于180度
//说明minA > maxA 且minA,maxA,velocityProjection同时小于零
minA += velocityProjection;
}
else
{
//速度与投影的夹角大于0度小于90度
//说明minA < maxA 且minA,maxA,velocityProjection同时大于零
maxA += velocityProjection;
}
// Do the same test as above for the new projection
//判断在当前帧的A物体移动后,是否与B相交
float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
if (intervalDistance > 0) result.WillIntersect = false;
// If the polygons are not intersecting and won't intersect, exit the loop
//如果当前帧不相交 且 移动后不相交 则表示两物体被分离
if (!result.Intersect && !result.WillIntersect) break;
// Check if the current interval distance is the minimum one. If so store
// the interval distance and the current distance.
// This will be used to calculate the minimum translation vector
//保存在所有边的单位法向量的投影向量中最小长度的相交向量
intervalDistance = Math.Abs(intervalDistance);
if (intervalDistance < minIntervalDistance)
{
minIntervalDistance = intervalDistance;
translationAxis = axis;
Vector d = polygonA.Center - polygonB.Center;
if (d.DotProduct(translationAxis) < 0) translationAxis = -translationAxis;
}
}
// The minimum translation vector can be used to push the polygons appart.
// First moves the polygons by their velocity
// then move polygonA by MinimumTranslationVector.
//如果移动后相交则计算要移动向量(例如像上图中的DG向量)
if (result.WillIntersect)
result.MinimumTranslationVector = translationAxis * minIntervalDistance;
return result;
}
// Calculate the distance between [minA, maxA] and [minB, maxB]
// The distance will be negative if the intervals overlap
//返回的值小于0.0表示轴分离A,B,大于0.0表示A,B在轴上的投影相交
public float IntervalDistance(float minA, float maxA, float minB, float maxB)
{
if (minA < minB)
{
return minB - maxA;
}
else
{
return minA - maxB;
}
}
// Calculate the projection of a polygon on an axis and returns it as a [min, max] interval
public void ProjectPolygon(Vector axis, Polygon polygon, ref float min, ref float max)
{
// To project a point on an axis use the dot product
float d = axis.DotProduct(polygon.Points[0]);
min = d;
max = d;
for (int i = 0; i < polygon.Points.Count; i++)
{
d = polygon.Points[i].DotProduct(axis);
if (d < min)
{
min = d;
}
else
{
if (d > max) { max = d;}
}
}
}
}
}
下面是实现的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace PolygonIntersection {
public partial class MainForm : Form
{
List<Polygon> polygons = new List<Polygon>();
Polygon player;
public MainForm()
{
InitializeComponent();
Paint += new PaintEventHandler(Form1_Paint);
KeyDown += new KeyEventHandler(Form1_KeyDown);
KeyPreview = true;
DoubleBuffered = true;
Polygon p = new Polygon();
p.Points.Add(new Vector(100, 0));
p.Points.Add(new Vector(150, 50));
p.Points.Add(new Vector(100, 150));
p.Points.Add(new Vector(0, 100));
polygons.Add(p);
p = new Polygon();
p.Points.Add(new Vector(50, 50));
p.Points.Add(new Vector(100, 0));
p.Points.Add(new Vector(150, 150));
p.Offset(80, 80);
polygons.Add(p);
p = new Polygon();
p.Points.Add(new Vector(300, 10));
p.Points.Add(new Vector(300, 20));
p.Points.Add(new Vector(310, 20));
polygons.Add(p);
p = new Polygon();
p.Points.Add(new Vector(0, 50));
p.Points.Add(new Vector(50,0));
p.Points.Add(new Vector(150,80));
p.Points.Add(new Vector(160,200));
p.Points.Add(new Vector(-10, 190));
p.Offset(300, 300);
polygons.Add(p);
foreach (Polygon polygon in polygons) polygon.BuildEdges();
player = polygons[0];
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Vector p1;
Vector p2;
foreach (Polygon polygon in polygons)
{
for (int i = 0; i < polygon.Points.Count; i++)
{
p1 = polygon.Points[i];
if (i + 1 >= polygon.Points.Count)
{
p2 = polygon.Points[0];
}
else
{
p2 = polygon.Points[i + 1];
}
e.Graphics.DrawLine(new Pen(Color.Black), p1, p2);
}
}
Invalidate();
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
int i = 14;
Vector velocity = new Vector();
#region
switch (e.KeyValue)
{
case 32: //SPACE
break;
case 38: // UP
velocity = new Vector(0, -i/10);
break;
case 40: // DOWN
velocity = new Vector(0, i/10);
break;
case 39: // RIGHT
velocity = new Vector(i, 0);
break;
case 37: // LEFT
velocity = new Vector(-i, 0);
break;
}
#endregion
Vector playerTranslation = velocity;
foreach (Polygon polygon in polygons)
{
if (polygon == player) continue;
PolygonCollisionResult r = PolygonCollision(player, polygon, velocity);
if (r.WillIntersect)
{
playerTranslation = velocity + r.MinimumTranslationVector;
break;
}
}
player.Offset(playerTranslation);
}
}
}
如果大家喜欢游戏编程技术,可以加我QQ282891168,我不喜欢加势利的人。