Cocos2d-x教程(34)-三维物体OBB碰撞检測算法
欢迎增加Cocos2d-x 交流群:193411763
转载时请注明原文出处 :http://blog.csdn.net/u012945598/article/details/39665911
----------------------------------------------------------------------------------------------------------------------------------------
在上一篇文章中解说了AABB包围盒碰撞检測的原理,并在文章末尾提到了Cocos2d-x 3.3beta0版本号中小乌龟碰撞检測的样例。这个样例使用的并不是是AABB碰撞检測,而是比AABB包围盒更加精确的OBB包围盒的碰撞检測方法,本篇文章将对OBB包围盒及其碰撞检測方法进行介绍。
1. OBB包围盒
简单来说,它就是一个能够旋转的AABB包围盒。在Cocos2d-x中使用过物理引擎的开发人员一定见过当我们在物理世界中创建一个物体并开启调试模式时。这个物体会被红色的矩形包围,当物体做平移或旋转时,这个红色矩形也会做相同的操作,这个红色矩形正是该物体的OBB包围盒。
如图1-1所看到的:
OBB包围盒的表达式:
Vec3 _center; // 中心点
/*
下面三个变量为正交单位向量。
定义了当前OBB包围盒的x,y,z轴
用于计算矢量投影
*/
Vec3 _xAxis; // 包围盒x轴方向单位矢量
Vec3 _yAxis; // 包围盒y轴方向单位矢量
Vec3 _zAxis; // 包围盒z轴方向单位矢量
Vec3 _extents; // 3个1/2边长,半长、半宽、半高
Cocos2d-x 3.0beta0在CCOBB.h中定义了五个成员变量,每个数据类型都是一个三维向量。包括了3个浮点数。
也就是说,表达一个OBB包围盒须要15个float类型的变量。占用60个字节,然而表示一个AABB包围盒仅须要两个顶点,24个字节。从这一点上来说。OBB的内存消耗算非常高了。
一种降低开销的方案是:仅仅存储旋转矩阵的两个轴。仅仅是在測试时利用叉积计算第三个轴,这样能够降低CPU操作开销并节省3个浮点数分量,降低20%内存消耗。
OBB包围盒的创建:
AABB aabb = _sprite->getAABB();//获取一个Sprite3D对象的aabb包围盒
OBB _obbt = OBB(aabb);//创建obb包围盒
2. OBB包围盒的碰撞检測方法
OBB包围盒的碰撞检測方法常採用的是分离轴定理,首先简单说明一下分离轴定理(separating axis theorem),通过分离轴定理我们能够得到例如以下结论:
而这条直线便成为分离线(在三维世界中被称为分离面),而且一定垂直于分离轴。
使用运算式来表达就是:|T * L|>rA + rB。在Cocos2d-x 3.3beta0中验证相交时採用的并不是这种方法。Cocos2d-x选用的方法是,取物体A及物体B的最大点和最小点的投影坐标,然后比較A的最大点与B的最小点,B的最小点与A的最大点。注意,虽然这是一个二维图像,可是实际上三维的图面体在分离轴上的投影跟二维的原理是一样的,都是一个线段。
所以实际上在取分离轴的时候,仅仅须要分别取第一个包围盒的3个坐标轴,第二个包围盒的3个坐标轴。以及垂直于某一轴的9个轴(其它的分离轴都是跟这15个分离轴中的某一个轴平行的轴,投影所得线段值都一样,无需再验证)。
如图2-2所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjk0NTU5OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
图中的x代表的就是包围盒的x轴方向边的矢量(注意这里写的是矢量,不是x,y,z坐标轴),当然这个方向的矢量有无数个,我们仅仅需取一个代表性的用来计算叉积就可以。
#ifndef __CC_OBB_H__
#define __CC_OBB_H__ #include "CCAABB.h"
#include "3d/3dExport.h" NS_CC_BEGIN class CC_3D_DLL OBB
{
public:
OBB();//默认构造函数 /*
* 构造函数 依据一个AABB包围盒初始化一个OBB包围盒
*/
OBB(const AABB& aabb); /*
* 构造函数 依据点信息初始化一个OBB包围盒
*/
OBB(const Vec3* verts, int num); /*
* 推断点是否在一个OBB包围盒内
*/
bool containPoint(const Vec3& point) const; /*
* 指定OBB各成员变量的值
*/
void set(const Vec3& center, const Vec3& _xAxis, const Vec3& _yAxis, const Vec3& _zAxis, const Vec3& _extents); /*
* 复位函数 将当前OBB对象所占用的内存块置零
*/
void reset(); /* 面向包围盒z轴负方向
* verts[0] : 左上点坐标
* verts[1] : 左下点坐标
* verts[2] : 右下点坐标
* verts[3] : 右上点坐标
*
* 面向包围盒z轴正方向
* verts[4] : 右上点坐标
* verts[5] : 右下点坐标
* verts[6] : 左下点坐标
* verts[7] : 左上点坐标
*/
void getCorners(Vec3* verts) const; /*
*检測是否和其它OBB盒碰撞
*/
bool intersects(const OBB& box) const; /**
* 由给定的变换矩阵变换OBB包围盒
*/
void transform(const Mat4& mat); protected:
/*
* 将点投影到目标轴
*/
float projectPoint(const Vec3& point, const Vec3& axis) const; /*
* 计算最大和最小投影值
*/
void getInterval(const OBB& box, const Vec3& axis, float &min, float &max) const; /*
* 取边的矢量 获取分离轴使用
*/
Vec3 getEdgeDirection(int index) const; /*
* 取面的方向矢量 获取分离轴时使用
*/
Vec3 getFaceDirection(int index) const; public:
Vec3 _center; // 中心点
/*
下面三个变量为正交单位向量,
定义了当前OBB包围盒的x,y,z轴
用于计算矢量投影
*/
Vec3 _xAxis; // 包围盒x轴方向单位矢量
Vec3 _yAxis; // 包围盒y轴方向单位矢量
Vec3 _zAxis; // 包围盒z轴方向单位矢量
Vec3 _extents; // 3个1/2边长,半长、半宽、半高
}; NS_CC_END #endif
CCOBB.cpp代码例如以下:
#include "3d/CCOBB.h" NS_CC_BEGIN #define ROTATE(a,i,j,k,l) g=a.m[i + 4 * j]; h=a.m[k + 4 * l]; a.m[i + 4 * j]=(float)(g-s*(h+g*tau)); a.m[k + 4 * l]=(float)(h+s*(g-h*tau)); //生成协方差矩阵
static Mat4 _getConvarianceMatrix(const Vec3* vertPos, int vertCount)
{
int i;
Mat4 Cov; double S1[3];
double S2[3][3]; S1[0] = S1[1] = S1[2] = 0.0;
S2[0][0] = S2[1][0] = S2[2][0] = 0.0;
S2[0][1] = S2[1][1] = S2[2][1] = 0.0;
S2[0][2] = S2[1][2] = S2[2][2] = 0.0; // get center of mass
for(i=0; i<vertCount; i++)
{
S1[0] += vertPos[i].x;
S1[1] += vertPos[i].y;
S1[2] += vertPos[i].z; S2[0][0] += vertPos[i].x * vertPos[i].x;
S2[1][1] += vertPos[i].y * vertPos[i].y;
S2[2][2] += vertPos[i].z * vertPos[i].z;
S2[0][1] += vertPos[i].x * vertPos[i].y;
S2[0][2] += vertPos[i].x * vertPos[i].z;
S2[1][2] += vertPos[i].y * vertPos[i].z;
} float n = (float)vertCount;
// now get covariances
Cov.m[0] = (float)(S2[0][0] - S1[0]*S1[0] / n) / n;
Cov.m[5] = (float)(S2[1][1] - S1[1]*S1[1] / n) / n;
Cov.m[10] = (float)(S2[2][2] - S1[2]*S1[2] / n) / n;
Cov.m[4] = (float)(S2[0][1] - S1[0]*S1[1] / n) / n;
Cov.m[9] = (float)(S2[1][2] - S1[1]*S1[2] / n) / n;
Cov.m[8] = (float)(S2[0][2] - S1[0]*S1[2] / n) / n;
Cov.m[1] = Cov.m[4];
Cov.m[2] = Cov.m[8];
Cov.m[6] = Cov.m[9]; return Cov;
}
//获取一个矢量在某个轴的分量
static float& _getElement( Vec3& point, int index)
{
if (index == 0)
return point.x;
if (index == 1)
return point.y;
if (index == 2)
return point.z; CC_ASSERT(0);
return point.x;
}
//获取特征向量
static void _getEigenVectors(Mat4* vout, Vec3* dout, Mat4 a)
{
int n = 3;
int j,iq,ip,i;
double tresh, theta, tau, t, sm, s, h, g, c;
int nrot;
Vec3 b;
Vec3 z;
Mat4 v;
Vec3 d; v = Mat4::IDENTITY;
for(ip = 0; ip < n; ip++)
{
_getElement(b, ip) = a.m[ip + 4 * ip];
_getElement(d, ip) = a.m[ip + 4 * ip];
_getElement(z, ip) = 0.0;
} nrot = 0; for(i = 0; i < 50; i++)
{
sm = 0.0;
for(ip = 0; ip < n; ip++) for(iq = ip+1; iq < n; iq++) sm += fabs(a.m[ip + 4 * iq]);
if( fabs(sm) < FLT_EPSILON )
{
v.transpose();
*vout = v;
*dout = d;
return;
} if (i < 3)
tresh = 0.2 * sm / (n*n);
else
tresh = 0.0; for(ip = 0; ip < n; ip++)
{
for(iq = ip + 1; iq < n; iq++)
{
g = 100.0 * fabs(a.m[ip + iq * 4]);
float dmip = _getElement(d, ip);
float dmiq = _getElement(d, iq); if( i>3 && fabs(dmip) + g == fabs(dmip) && fabs(dmiq) + g == fabs(dmiq) )
{
a.m[ip + 4 * iq] = 0.0;
}
else if (fabs(a.m[ip + 4 * iq]) > tresh)
{
h = dmiq - dmip;
if (fabs(h) + g == fabs(h))
{
t=(a.m[ip + 4 * iq])/h;
}
else
{
theta = 0.5 * h / (a.m[ip + 4 * iq]);
t=1.0 / (fabs(theta) + sqrt(1.0 + theta * theta));
if (theta < 0.0) t = -t;
}
c = 1.0 / sqrt(1+t*t);
s = t*c;
tau = s / (1.0+c);
h = t * a.m[ip + 4 * iq];
_getElement(z, ip) -= (float)h;
_getElement(z, iq) += (float)h;
_getElement(d, ip) -= (float)h;
_getElement(d, iq) += (float)h;
a.m[ip + 4 * iq]=0.0;
for(j = 0; j < ip; j++) { ROTATE(a,j,ip,j,iq); }
for(j = ip + 1; j < iq; j++) { ROTATE(a,ip,j,j,iq); }
for(j = iq + 1; j < n; j++) { ROTATE(a,ip,j,iq,j); }
for(j = 0; j < n; j++) { ROTATE(v,j,ip,j,iq); }
nrot++;
}
}
} for(ip = 0; ip < n; ip++)
{
_getElement(b, ip) += _getElement(z, ip);
_getElement(d, ip) = _getElement(b, ip);
_getElement(z, ip) = 0.0f;
}
} v.transpose();
*vout = v;
*dout = d;
return;
}
//创建OBB包围盒取向矩阵
static Mat4 _getOBBOrientation(const Vec3* vertPos, int num)
{
Mat4 Cov; //创建一个 4*4 矩阵 if (num <= 0)
return Mat4::IDENTITY; //返回单位矩阵 Cov = _getConvarianceMatrix(vertPos, num); //创建协方差矩阵 // now get eigenvectors
Mat4 Evecs;
Vec3 Evals;
_getEigenVectors(&Evecs, &Evals, Cov); //求特征向量 Evecs.transpose(); //转置 return Evecs;
}
//默认构造函数
OBB::OBB()
{
//数据复位
reset();
}
//由一个AABB包围盒生成OBB包围盒
OBB::OBB(const AABB& aabb)
{
//数据复位
reset(); //中心点
_center = (aabb._min + aabb._max);
_center.scale(0.5f); //各轴旋转矩阵的单位矩阵
_xAxis = Vec3(1.0f, 0.0f, 0.0f);
_yAxis = Vec3(0.0f, 1.0f, 0.0f);
_zAxis = Vec3(0.0f, 0.0f, 1.0f); //半尺存 半长 半宽 半高
_extents = aabb._max - aabb._min;
_extents.scale(0.5f);
}
//构造函数 依据点信息初始化一个OBB包围盒
OBB::OBB(const Vec3* verts, int num)
{
if (!verts) return; //假设verts不存在 返回 reset(); //数据复位 Mat4 matTransform = _getOBBOrientation(verts, num); //创建包围盒取向矩阵 /*
matTransform是一个正交矩阵,所以它的逆矩阵就是它的转置;
AA'=E(E为单位矩阵。A'表示“矩阵A的转置矩阵”) A称为正交矩阵
*/
matTransform.transpose(); //计算matTransform矩阵的转置(此处相当于求逆矩) Vec3 vecMax = matTransform * Vec3(verts[0].x, verts[0].y, verts[0].z); Vec3 vecMin = vecMax; for (int i = 1; i < num; i++)
{
Vec3 vect = matTransform * Vec3(verts[i].x, verts[i].y, verts[i].z); vecMax.x = vecMax.x > vect.x ? vecMax.x : vect.x;
vecMax.y = vecMax.y > vect.y ? vecMax.y : vect.y;
vecMax.z = vecMax.z > vect.z ? vecMax.z : vect.z; vecMin.x = vecMin.x < vect.x ? vecMin.x : vect.x;
vecMin.y = vecMin.y < vect.y ? vecMin.y : vect.y;
vecMin.z = vecMin.z < vect.z ? vecMin.z : vect.z;
} matTransform.transpose(); _xAxis = Vec3(matTransform.m[0], matTransform.m[1], matTransform.m[2]);
_yAxis = Vec3(matTransform.m[4], matTransform.m[5], matTransform.m[6]);
_zAxis = Vec3(matTransform.m[8], matTransform.m[9], matTransform.m[10]); _center = 0.5f * (vecMax + vecMin);
_center *= matTransform; _xAxis.normalize();
_yAxis.normalize();
_zAxis.normalize(); _extents = 0.5f * (vecMax - vecMin);
}
//推断一点是否在OBB包围盒内
bool OBB::containPoint(const Vec3& point) const
{
//相当于将点坐标从世界坐标系中转换到了OBB包围盒的物体坐标系中
Vec3 vd = point - _center; /*
dot方法为求点积
因为_xAxis为单位矢量
vd与_xAxis的点击即为在_xAxis方向的投影
*/
float d = vd.dot(_xAxis); //计算x方向投影d //推断投影是否大于x正方向的半长或小于x负方向半长
if (d > _extents.x || d < -_extents.x)
return false;//满足条件说明不在包围盒内 d = vd.dot(_yAxis); //计算y方向投影
//同理
if (d > _extents.y || d < -_extents.y)
return false; d = vd.dot(_zAxis);//计算z方向投影
if (d > _extents.z || d < -_extents.z)
return false; return true;
}
//指定OBB包围盒的变量值
void OBB::set(const Vec3& center, const Vec3& xAxis, const Vec3& yAxis, const Vec3& zAxis, const Vec3& extents)
{
_center = center;
_xAxis = xAxis;
_yAxis = yAxis;
_zAxis = zAxis;
_extents = extents;
}
//复位
void OBB::reset()
{
memset(this, 0, sizeof(OBB)); //将OBB所在内存块置零
}
//获取顶点信息
void OBB::getCorners(Vec3* verts) const
{
Vec3 extX = _xAxis * _extents.x; //x方向分量
Vec3 extY = _yAxis * _extents.y; //y方向分量
Vec3 extZ = _zAxis * _extents.z; //z方向分量 //z轴正方向的面
verts[0] = _center - extX + extY + extZ; // 左上顶点坐标
verts[1] = _center - extX - extY + extZ; // 左下顶点坐标
verts[2] = _center + extX - extY + extZ; // 右下顶点坐标
verts[3] = _center + extX + extY + extZ; // 右上顶点坐标 //z轴负方向的面
verts[4] = _center + extX + extY - extZ; // 右上顶点坐标
verts[5] = _center + extX - extY - extZ; // 右下顶点坐标
verts[6] = _center - extX - extY - extZ; // 左下顶点坐标
verts[7] = _center - extX + extY - extZ; // 左上顶点坐标
}
//将点投影到坐标轴
float OBB::projectPoint(const Vec3& point, const Vec3& axis)const
{
float dot = axis.dot(point); //点积
float ret = dot * point.length();
return ret;
}
//计算最大最小投影值
void OBB::getInterval(const OBB& box, const Vec3& axis, float &min, float &max)const
{
Vec3 corners[8];
box.getCorners(corners);//获取包围盒顶点信息
float value;
//分别投影八个点。取最大和最小值
min = max = projectPoint(axis, corners[0]);
for(int i = 1; i < 8; i++)
{
value = projectPoint(axis, corners[i]);
min = MIN(min, value);
max = MAX(max, value);
}
}
//取边的矢量
Vec3 OBB::getEdgeDirection(int index)const
{
Vec3 corners[8];
getCorners(corners); //获取八个顶点信息 Vec3 tmpLine;
switch(index)
{
case 0:// x轴方向
tmpLine = corners[5] - corners[6];
tmpLine.normalize(); //归一化
break;
case 1:// y轴方向
tmpLine = corners[7] - corners[6];
tmpLine.normalize();
break;
case 2:// z轴方向
tmpLine = corners[1] - corners[6];
tmpLine.normalize();
break;
default:
CCASSERT(0, "Invalid index!");
break;
}
return tmpLine;
}
//取面的方向矢量
Vec3 OBB::getFaceDirection(int index) const
{
Vec3 corners[8];
getCorners(corners); //获取八个顶点信息 Vec3 faceDirection, v0, v1;
switch(index)
{
case 0://前/后 计算结果为一个与z轴平行的矢量
v0 = corners[2] - corners[1]; //朝向+z的面 左下点->右下点的矢量
v1 = corners[0] - corners[1]; // 左下点->左上点的矢量
/*
两个矢量的叉积得到的结果
是垂直于原来两个相乘矢量的矢量
*/
Vec3::cross(v0, v1, &faceDirection); //计算v0,v1的叉积 结果存储到faceDirection
/*
归一化
此处相当于求x,y轴所在平面的法矢量
*/
faceDirection.normalize();
break;
case 1:// 左/右 计算结果为一个与x轴平行的矢量
v0 = corners[5] - corners[2];
v1 = corners[3] - corners[2];
Vec3::cross(v0, v1, &faceDirection);
faceDirection.normalize();
break;
case 2:// 上/下 计算结果为一个与y轴平行的矢量
v0 = corners[1] - corners[2];
v1 = corners[5] - corners[2];
Vec3::cross(v0, v1, &faceDirection);
faceDirection.normalize();
break;
default:
CCASSERT(0, "Invalid index!");
break;
}
return faceDirection; //返回方向矢量
}
//检測两个OBB包围盒是否重合
bool OBB::intersects(const OBB& box) const
{
float min1, max1, min2, max2;
//当前包围盒的三个面方向 相当于取包围盒的三个坐标轴为分离轴并计算投影作比較
for (int i = 0; i < 3; i++)
{
getInterval(*this, getFaceDirection(i), min1, max1);//计算当前包围盒在某轴上的最大最小投影值
getInterval(box, getFaceDirection(i), min2, max2);//计算还有一个包围盒在某轴上的最大最小投影值
if (max1 < min2 || max2 < min1) return false; //推断分离轴上投影是否重合
}
//box包围盒的三个面方向
for (int i = 0; i < 3; i++)
{
getInterval(*this, box.getFaceDirection(i), min1, max1);
getInterval(box, box.getFaceDirection(i), min2, max2);
if (max1 < min2 || max2 < min1) return false;
} for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
Vec3 axis;
//Vec3::cross(getFaceDirection(i), box.getFaceDirection(j), &axis); //2d-x源代码
Vec3::cross(getEdgeDirection(i), box.getEdgeDirection(j), &axis); //改动,这里应该边的矢量并做叉积
getInterval(*this, axis, min1, max1);
getInterval(box, axis, min2, max2);
if (max1 < min2 || max2 < min1) return false;
}
} return true;
} //由一个给定矩阵对OBB包围盒进行变换
void OBB::transform(const Mat4& mat)
{
// 新的中心点
Vec4 newcenter = mat * Vec4(_center.x, _center.y, _center.z, 1.0f);
_center.x = newcenter.x;
_center.y = newcenter.y;
_center.z = newcenter.z; //变换向量
_xAxis = mat * _xAxis;
_yAxis = mat * _yAxis;
_zAxis = mat * _zAxis; _xAxis.normalize(); //归一化
_yAxis.normalize();
_zAxis.normalize(); Vec3 scale, trans;
Quaternion quat; //四元数 单位长度的四元数能够表示三维旋转
mat.decompose(&scale, &quat, &trans); //半长 半宽 半高
_extents.x *= scale.x;
_extents.y *= scale.y;
_extents.z *= scale.z;
} NS_CC_END
Cocos2d-x教程(34)-三维物体OBB碰撞检測算法的更多相关文章
- Cocos2d-x教程(35)-三维拾取Ray-AABB碰撞检測算法
欢迎增加Cocos2d-x 交流群:193411763 转载时请注明原文出处 :http://blog.csdn.net/u012945598/article/details/39927911 --- ...
- cocos2d-x 3.0游戏实例学习笔记 《跑酷》第七步--物理碰撞检測(1)
说明:这里是借鉴:晓风残月前辈的博客,他是将泰然网的跑酷教程,用cocos2d-x 2.X 版本号重写的,眼下我正在学习cocos2d-X3.0 于是就用cocos2d-X 3.0重写,并做相关笔记 ...
- cocos2d-x ios游戏开发初认识(八) 触摸事件与碰撞检測
玩过植物大战僵尸都知道,要在草坪里放一朵向日葵或者其他的植物仅仅需触摸那个植物将其拖入到想要摆放的位置,这事实上就是这节要写的触摸事件.还能够发现当我们的僵尸出来的时候,我们的小豌豆会发子弹攻击僵尸, ...
- Cocos2d-x3.0游戏实例之《别救我》第七篇——物理世界的碰撞检測
事实上我也非常吃惊-居然写到第七篇了,我估计也就是四篇的内容,感觉非常奇妙,我也不会非常唠叨什么吖); // 0001 ); // 0001 ); // 0001 这样我们才干监听到它们的碰 ...
- 碰撞检測之OBB-OBB的SweepTest
提要 当物体在运动的时候.普通的每帧进行碰撞检測已经无法满足要求,比方子弹的运动 两帧的位置已经直接将中间的板子穿过了,所以 t 时刻和 t +1 时刻的检測都是失效的.这时候须要用到的就是sweep ...
- 实例介绍Cocos2d-x中Box2D物理引擎:碰撞检測
在Box2D中碰撞事件通过实现b2ContactListener类函数实现,b2ContactListener是Box2D提供的抽象类,它的抽象函数:virtual void BeginContact ...
- Cocos2d-x碰撞检測
假设不适用Box2D物理引擎.那么要进行Cocos2d-x的碰撞检測那我们的方法往往就是进行"矩形和点"."矩形和矩形"这样粗略的碰撞检測.我们一般採取开启sc ...
- Cocos2d-x 精灵碰撞检測(方法二)
将"Cocos2d-x 精灵碰撞检測(方法一)" update函数改动一下. 使用精灵boundingBox函数获取直接精灵边界框, 不用自己计算精灵矩形大小了,还比較精确,然后调 ...
- cocos2d-x 旅程開始--(实现瓦片地图中的碰撞检測)
转眼隔了一天了,昨天搞了整整一下午加一晚上,楞是没搞定小坦克跟砖头的碰撞检測,带着个问题睡觉甚是难受啊!还好今天弄成功了.只是感觉程序不怎么稳定啊.并且发现自己写的东西让我重写一遍的话我肯定写不出来. ...
随机推荐
- 最简单的SPA(单页应用)实现
$(function(){ var replacePage = function(href, onFinish){ $.get(href,{},function(raw){ var data = ra ...
- Java使用 POI 操作Excel
Java中常见的用来操作 Excel 的方式有2种:JXL和POI.JXL只能对 Excel进行操作,且只支持到 Excel 95-2000的版本.而POI是Apache 的开源项目,由Java编写的 ...
- windows server 2008 如何查看异常重启日志
下面蓝队网络为大家介绍下windows server 2008 如何查看异常重启日志 开始->管理工具->时间查看器 windows日志->系统 筛选当前日志 选择Kernel-Po ...
- Farseer.net轻量级开源框架 入门篇:Where条件的终极使用
导航 目 录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 入门篇: 查询数据详解 下一篇:Farseer.net轻量级开源框架 中级篇: 事务的使用 ...
- MFC_2.5 选项卡控件的使用
选项卡控件的使用 1.新建默认MFC文件. 2.资源-添加Dialog-添加类.(假设生成3个,Dialog1Dialog2Dialog3) 3.类向导,添加类,点小三角形,添加MFC类.添加CTab ...
- java学习_5_23
Collection接口中定义的方法如下,所有继承自Collection接口的接口(List,Set)的实现类均实现了这些方法. List容器是有序.可重复的,常用的实现类:ArrayList,Lin ...
- java.net.MalformedURLException: no protocol: www.baidu.com
URL url = new URL("www.baidu.com");改为 URL url = new URL("http://www.baidu.com");
- 洛谷——P2171 Hz吐泡泡
P2171 Hz吐泡泡 题目描述 这天,Hz大大心血来潮,吐了n个不同的泡泡玩(保证没有重复的泡泡).因为他还要写作业,所以他请你帮他把这些泡泡排序成树(左子树<=根<右子树).输出它的后 ...
- 深入理解PHP之strpos
概述 在php中经常用 strpos 判断字符串是否在另一个字符串中存在, 本文介绍 strpos 函数及其实现. strpos应用 <?php /* strpos示例 */ // test e ...
- python3支持excel读写
1.安装setuptools-17.0.tar.gz cmd 进入命令行 cd C:\Users\vivi\Desktop\pythonforexcel\setuptools-17.0\setupto ...