NeHe OpenGL教程 第三十課:碰撞檢測

轉自【翻譯】NeHe OpenGL 教程

前言

聲明,此?NeHe OpenGL教程系列文章由51博客yarin翻譯(2010-08-19),本博客為轉載并稍加整理與修改。對NeHe的OpenGL管線教程的編寫,以及yarn的翻譯整理表示感謝。

?

NeHe OpenGL第三十課:碰撞檢測

碰撞檢測:

這是一課激動的教程,你也許等待它多時了。你將學會碰撞剪裁,物理模擬太多的東西,慢慢期待吧。
?
碰撞檢測和物理模擬(作者:Dimitrios Christopoulos (christop@fhw.gr))

碰撞檢測

這是一個我遇到的最困難的題目,因為它沒有一個簡單的解決辦法.對于每一個程序都有一種檢測碰撞的方法.當然這里有一種蠻力,它適用于各種不同的應用,當它非常的費時.
我們將講述一種算法,它非常的快,簡單并易于擴展.下面我們來看看這個算法包含的內容:

1) 碰撞檢測?
移動的球-平面?
移動的球-圓柱?
移動的球-移動的球?
2) 基于物理的建模?
碰撞表示?
應用重力加速度?
3) 特殊效果?
爆炸的表示,利用互交叉的公告板形式?
聲音使用Windows聲音庫?
4) 關于代碼?
代碼被分為以下5個部分?
Lesson30.cpp?? : 主程序代碼l?
Image.cpp, Image.h : 加載圖像?
Tmatrix.cpp, Tmatrix.h : 矩陣?
Tray.cpp, Tray.h : 射線?
Tvector.cpp, Tvector.h : 向量

1) 碰撞檢測

我們使用射線來完成相關的算法,它的定義為:

射線上的點 = 射線的原點+ t * 射線的方向

t 用來描述它距離原點的位置,它的范圍是[0, 無限遠).

現在我們可以使用射線來計算它和平面以及圓柱的交點了。

射線和平面的碰撞檢測:

平面被描述為:

Xn dot X = d

Xn 是平面的法線.
X 是平面上的一個點.
d 是平面到原點的距離.

現在我們得到射線和平面的兩個方程:

PointOnRay = Raystart + t * Raydirection
Xn dot X = d

如果他們相交,則上訴方程組有解,如下所示:

Xn dot PointOnRay = d

(Xn dot Raystart) + t * (Xn dot Raydirection) = d

解得 t:

t = (d - Xn dot Raystart) / (Xn dot Raydirection)

t代表原點到與平面相交點的參數,把t帶回原方程我們會得到與平面的碰撞點.如果Xn*Raydirection=0。則說明它與平面平行,則將不產生碰撞。如果t為負值,則說明交點在射線的相反方向,也不會產生碰撞。
??
//判斷是否和平面相交,是則返回1,否則返回0int TestIntersionPlane(const Plane& plane,const TVector& position,const TVector& direction, double& lamda, TVector&

pNormal){
double DotProduct=direction.dot(plane._Normal);
double l2;

//判斷是否平行于平面
if ((DotProduct<ZERO)&&(DotProduct>-ZERO))?
return 0;

l2=(plane._Normal.dot(plane._Position-position))/DotProduct;

if (l2<-ZERO)?
return 0;

pNormal=plane._Normal;
lamda=l2;
return 1;
}

射線-圓柱的碰撞檢測

計算射線和圓柱方程組得解。??
???
int TestIntersionCylinder(const Cylinder& cylinder,const TVector& position,const TVector& direction, double& lamda, TVector& pNormal,TVector& newposition)

球-球之間的碰撞檢測

球被表示為中心和它的半徑,決定兩個球是否相交就是求出它們之間的距離是否小于它們的直徑。

在處理兩個移動的球是否相交時,有一個bug就是,當它們的移動速度太快,回出現它們相交,但在相鄰的兩步檢測不出它們是否相交的情況,如下圖所示:


有一個替代的辦法就是細分相鄰的時間片斷,如果在這之間發生了碰撞,則確定有效。我們把這個細分時間段設置為3,代碼如下:???
???
//判斷球和球是否相交,是則返回1,否則返回0int FindBallCol(TVector& point, double& TimePoint, double Time2, int& BallNr1, int& BallNr2){?TVector RelativeV;?TRay rays;?double MyTime=0.0, Add=Time2/150.0, Timedummy=10000, Timedummy2=-1;?TVector posi;??//判斷球和球是否相交?for (int i=0;i<NrOfBalls-1;i++)?{? for (int j=i+1;j<NrOfBalls;j++)? {?????? RelativeV=ArrayVel[i]-ArrayVel[j];???rays=TRay(OldPos[i],TVector::unit(RelativeV));???MyTime=0.0;
if ( (rays.dist(OldPos[j])) > 40) continue;

while (MyTime<Time2)
{
MyTime+=Add;
posi=OldPos[i]+RelativeV*MyTime;
if (posi.dist(OldPos[j])<=40) {
point=posi;
if (Timedummy>(MyTime-Add)) Timedummy=MyTime-Add;
BallNr1=i;
BallNr2=j;
break;
}

}
}

}

if (Timedummy!=10000) { TimePoint=Timedummy;
return 1;
}

return 0;
}

怎樣應用我們的知識

現在我們已經可以決定射線和平面/圓柱的交點了,如下圖所示:


當我們找到了碰撞位置后,下一步我們需要知道它是否發生在當前這一步中.如果距離碰撞點的位置小于這一步球體運動的間隔,則碰撞發生.我們使用如下的方程計算運動到碰撞時所需的時間:
Tc= Dsc*T / Dst?
接著我們知道碰撞點位置,如下面公式所示:
Collision point= Start + Velocity*Tc

2) 基于物理的模擬

碰撞反應

為了計算對于一個靜止物體的碰撞,我們需要知道以下信息:碰撞點,碰撞法線,碰撞時間.

它是基于以下物理規律的,碰撞的入射角等于反射角.如下圖所示:


R 為反射方向
I 為入射方向
N 為法線方向

反射方向有以下公式計算 :

R= 2*(-I dot N)*N + I?
??
rt2=ArrayVel[BallNr].mag();??????// 返回速度向量的模
ArrayVel[BallNr].unit();??????// 歸一化速度向量

// 計算反射向量
ArrayVel[BallNr]=TVector::unit( (normal*(2*normal.dot(-ArrayVel[BallNr]))) + ArrayVel[BallNr] );
ArrayVel[BallNr]=ArrayVel[BallNr]*rt2;?????

球體之間的碰撞

由于它很復雜,我們用下圖來說明這個原理.??

U1和U2為速度向量,我們用X_Axis表示兩個球中心連線的軸,U1X和U2X為U1和U2在這個軸上的分量。U1y和U2y為垂直于X_Axis軸的分量。M1和M2為兩個球體的分量。V1和V2為碰撞后的速度,V1x,V1y,V2x,V2y為他們的分量。

在我們的例子里,所有球的質量都相等,解得方程為,在垂直軸上的速度不變,在X_Axis軸上互相交換速度。代碼如下:
??
TVector pb1,pb2,xaxis,U1x,U1y,U2x,U2y,V1x,V1y,V2x,V2y;
double a,b;
pb1=OldPos[BallColNr1]+ArrayVel[BallColNr1]*BallTime;???// 球1的位置
pb2=OldPos[BallColNr2]+ArrayVel[BallColNr2]*BallTime;???// 球2的位置
xaxis=(pb2-pb1).unit();???????// X-Axis軸
a=xaxis.dot(ArrayVel[BallColNr1]);?????// X_Axis投影系數
U1x=xaxis*a;????????// 計算在X_Axis軸上的速度
U1y=ArrayVel[BallColNr1]-U1x; // 計算在垂直軸上的速度
xaxis=(pb1-pb2).unit();???????
b=xaxis.dot(ArrayVel[BallColNr2]);?????
U2x=xaxis*b;????????
U2y=ArrayVel[BallColNr2]-U2x;
V1x=(U1x+U2x-(U1x-U2x))*0.5;??????// 計算新的速度
V2x=(U1x+U2x-(U2x-U1x))*0.5;
V1y=U1y;
V2y=U2y;
for (j=0;j<NrOfBalls;j++)??????// 更新所有球的位置
ArrayPos[j]=OldPos[j]+ArrayVel[j]*BallTime;
ArrayVel[BallColNr1]=V1x+V1y;??????// 設置新的速度
ArrayVel[BallColNr2]=V2x+V2y;??????
???
萬有引力的模擬

我們使用歐拉方程來模擬萬有引力,如下所示:?
Velocity_New = Velovity_Old + Acceleration*TimeStep
Position_New = Position_Old + Velocity_New*TimeStep

在每次模擬中,我們用上面公式計算的速度取代舊的速度

3) 特殊效果

爆炸

最好的表示爆炸效果的就是使用兩個互相垂直的平面,并使用alpha混合在窗口中顯示它們。接著讓alpha變為0,設定爆炸效果不可見。代碼如下所示:??
???
// 渲染/混合爆炸效果
glEnable(GL_BLEND);???????// 使用混合
glDepthMask(GL_FALSE);???????// 禁用深度緩存
glBindTexture(GL_TEXTURE_2D, texture[1]);????// 設置紋理
for(i=0; i<20; i++)???????// 渲染20個爆炸效果
{
?if(ExplosionArray[i]._Alpha>=0)
?{
??glPushMatrix();
??ExplosionArray[i]._Alpha-=0.01f;???// 設置alpha
??ExplosionArray[i]._Scale+=0.03f;???// 設置縮放
??// 設置顏色
??glColor4f(1,1,0,ExplosionArray[i]._Alpha);??
??glScalef(ExplosionArray[i]._Scale,ExplosionArray[i]._Scale,ExplosionArray[i]._Scale);
??// 設置位置
??glTranslatef((float)ExplosionArray[i]._Position.X()/ExplosionArray[i]._Scale,
???(float)ExplosionArray[i]._Position.Y()/ExplosionArray[i]._Scale,
???(float)ExplosionArray[i]._Position.Z()/ExplosionArray[i]._Scale);
??glCallList(dlist);?????// 調用顯示列表繪制爆炸效果
??glPopMatrix();
?}
}

聲音

在Windows下我們簡單的調用PlaySound()函數播放聲音。

4) 代碼的流程

如果你成功的讀完了理論部分,在你開始運行程序并播放聲音以前。我們將用偽代碼向你介紹一些整個流程,以便你能成功的看懂代碼。??
???
While (Timestep!=0)
{
?對每一個球
?{
??計算最近的與平面碰撞的位置;
??計算最近的與圓柱碰撞的位置;
??如果碰撞發生,則保存并替換最近的碰撞點;
?}
?檢測各個球之間的碰撞;
?如果碰撞發生,則保存并替換最近的碰撞點;

?If (碰撞發生)
?{
??移動所有的球道碰撞點的時間;
??(We already have computed the point, normal and collision time.)
??計算碰撞后的效果;
??Timestep-=CollisonTime;
?}
?else
??移動所有的球體一步
}

下面是對上面偽代碼的實現:
??
//模擬函數,計算碰撞檢測和物理模擬void idle(){? double rt,rt2,rt4,lamda=10000;? TVector norm,uveloc;? TVector normal,point,time;? double RestTime,BallTime;? TVector Pos2;? int BallNr=0,dummy=0,BallColNr1,BallColNr2;? TVector Nc;
//如果沒有鎖定到球上,旋轉攝像機
if (!hook_toball1)
{
camera_rotation+=0.1f;
if (camera_rotation>360)
camera_rotation=0;
}

RestTime=Time;
lamda=1000;

//計算重力加速度
for (int j=0;j<NrOfBalls;j++)
ArrayVel[j]+=accel*RestTime;

//如果在一步的模擬時間內(如果來不及計算,則跳過幾步)
while (RestTime>ZERO)
{
lamda=10000;

//對于每個球,找到它們最近的碰撞點
for (int i=0;i<NrOfBalls;i++)
{
//計算新的位置和移動的距離
OldPos[i]=ArrayPos[i];
TVector::unit(ArrayVel[i],uveloc);
ArrayPos[i]=ArrayPos[i]+ArrayVel[i]*RestTime;
rt2=OldPos[i].dist(ArrayPos[i]);

//測試是否和墻面碰撞
if (TestIntersionPlane(pl1,OldPos[i],uveloc,rt,norm))
{?
//計算碰撞的時間
rt4=rt*RestTime/rt2;

//如果小于當前保存的碰撞時間,則更新它
if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=OldPos[i]+uveloc*rt;
lamda=rt4;
BallNr=i;
}
}
}

if (TestIntersionPlane(pl2,OldPos[i],uveloc,rt,norm))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=OldPos[i]+uveloc*rt;
lamda=rt4;
BallNr=i;
dummy=1;
}
}

}

if (TestIntersionPlane(pl3,OldPos[i],uveloc,rt,norm))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=OldPos[i]+uveloc*rt;
lamda=rt4;
BallNr=i;
}
}
}

if (TestIntersionPlane(pl4,OldPos[i],uveloc,rt,norm))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=OldPos[i]+uveloc*rt;
lamda=rt4;
BallNr=i;
}
}
}

if (TestIntersionPlane(pl5,OldPos[i],uveloc,rt,norm))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=OldPos[i]+uveloc*rt;
lamda=rt4;
BallNr=i;
}
}
}

//測試是否與三個圓柱相碰
if (TestIntersionCylinder(cyl1,OldPos[i],uveloc,rt,norm,Nc))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=Nc;
lamda=rt4;
BallNr=i;
}
}

}
if (TestIntersionCylinder(cyl2,OldPos[i],uveloc,rt,norm,Nc))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=Nc;
lamda=rt4;
BallNr=i;
}
}

}
if (TestIntersionCylinder(cyl3,OldPos[i],uveloc,rt,norm,Nc))
{
rt4=rt*RestTime/rt2;

if (rt4<=lamda)
{?
if (rt4<=RestTime+ZERO)
if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
{
normal=norm;
point=Nc;
lamda=rt4;
BallNr=i;
}
}

}
}

//計算每個球之間的碰撞,如果碰撞時間小于與上面的碰撞,則替換它們
if (FindBallCol(Pos2,BallTime,RestTime,BallColNr1,BallColNr2))
{
if (sounds)
PlaySound("Data/Explode.wav",NULL,SND_FILENAME|SND_ASYNC);

if ( (lamda==10000) || (lamda>BallTime) )
{
RestTime=RestTime-BallTime;

TVector pb1,pb2,xaxis,U1x,U1y,U2x,U2y,V1x,V1y,V2x,V2y;
double a,b;

pb1=OldPos[BallColNr1]+ArrayVel[BallColNr1]*BallTime;
pb2=OldPos[BallColNr2]+ArrayVel[BallColNr2]*BallTime;
xaxis=(pb2-pb1).unit();

a=xaxis.dot(ArrayVel[BallColNr1]);
U1x=xaxis*a;
U1y=ArrayVel[BallColNr1]-U1x;

xaxis=(pb1-pb2).unit();
b=xaxis.dot(ArrayVel[BallColNr2]);
U2x=xaxis*b;
U2y=ArrayVel[BallColNr2]-U2x;

V1x=(U1x+U2x-(U1x-U2x))*0.5;
V2x=(U1x+U2x-(U2x-U1x))*0.5;
V1y=U1y;
V2y=U2y;

for (j=0;j<NrOfBalls;j++)
ArrayPos[j]=OldPos[j]+ArrayVel[j]*BallTime;

ArrayVel[BallColNr1]=V1x+V1y;
ArrayVel[BallColNr2]=V2x+V2y;

//Update explosion array
for(j=0;j<20;j++)
{
if (ExplosionArray[j]._Alpha<=0)
{
ExplosionArray[j]._Alpha=1;
ExplosionArray[j]._Position=ArrayPos[BallColNr1];
ExplosionArray[j]._Scale=1;
break;
}
}

continue;
}
}

//最后的測試,替換下次碰撞的時間,并更新爆炸效果的數組
if (lamda!=10000)
{?
?RestTime-=lamda;

?for (j=0;j<NrOfBalls;j++)
??ArrayPos[j]=OldPos[j]+ArrayVel[j]*lamda;

?rt2=ArrayVel[BallNr].mag();
?ArrayVel[BallNr].unit();
?ArrayVel[BallNr]=TVector::unit( (normal*(2*normal.dot(-ArrayVel[BallNr]))) + ArrayVel[BallNr] );
?ArrayVel[BallNr]=ArrayVel[BallNr]*rt2;

?for(j=0;j<20;j++)
?{
??if (ExplosionArray[j]._Alpha<=0)
??{
???ExplosionArray[j]._Alpha=1;
???ExplosionArray[j]._Position=point;
???ExplosionArray[j]._Scale=1;
???break;
??}
?}
}
else
?RestTime=0;
}
}
原文及其個版本源代碼下載:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=30

沒有整理與歸納的知識,一文不值!高度概括與梳理的知識,才是自己真正的知識與技能。 永遠不要讓自己的自由、好奇、充滿創造力的想法被現實的框架所束縛,讓創造力自由成長吧! 多花時間,關心他(她)人,正如別人所關心你的。理想的騰飛與實現,沒有別人的支持與幫助,是萬萬不能的。







本文轉自wenglabs博客園博客,原文鏈接:http://www.cnblogs.com/arxive/p/6239517.html,如需轉載請自行聯系原作者



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/393046.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/393046.shtml
英文地址,請注明出處:http://en.pswp.cn/news/393046.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

andorid手機電腦操作

之前一直使用androidscreencast在pc上對手機進行操作,好久都沒用了,前些天再次用的時候,提演示樣例如以下: 決定還是自己寫一個吧,由于7月份要做一個小分享,打算講一些android的東西,須要在電腦上顯示手機這邊的畫面,提供一定的操作. 花了一點時間做好了,給大家截一個圖,代碼放…

struct.error: cannot convert argument to integer解決辦法

更新Python包轉載于:https://www.cnblogs.com/long5683/p/11086768.html

sphinx_Sphinx之謎:如何輕松地編寫代碼

sphinx為什么我在這里&#xff1f; (Why Am I Here?) You, the reader, are here because you wrote some awesome tool in Python, and you want to make it accessible and easy to use.讀者之所以在這里&#xff0c;是因為您使用Python編寫了一些很棒的工具&#xff0c;并且…

linux貪吃蛇c程序,Linux環境下C語言實現貪吃蛇游戲

Linux環境下C語言實現貪吃蛇游戲[liultest snake]$ more snake.c#include #include #include #include #include #define NUM 60struct direct //用來表示方向的{int cx;int cy;};typedef struct node //鏈表的結點{int cx;int cy;struct node *back;struct node *next;}node;v…

Java正則表達式的使用和詳解(上)

1.匹配驗證-驗證Email是否正確 public static void main(String[] args) {// 要驗證的字符串String str "servicexsoftlab.net";// 郵箱驗證規則String regEx "[a-zA-Z_]{1,}[0-9]{0,}(([a-zA-z0-9]-*){1,}\\.){1,3}[a-zA-z\\-]{1,}";// 編譯正則表達式P…

在組策略中使用腳本為域用戶添加網絡打印機

使用腳本為用戶添加網絡打印機 如果你想讓培訓部門的用戶登錄后就能添加網絡打印機&#xff0c;就可以使用登錄腳本來實現。其中DCServer是域控制&#xff0c;MarketPC1是市場部門的計算機&#xff0c;韓立輝用戶是培訓部門的用戶。下面就驗證使用組策略為培訓部門的用戶添加網…

leetcode257. 二叉樹的所有路徑(回溯算法)

給定一個二叉樹&#xff0c;返回所有從根節點到葉子節點的路徑。 說明: 葉子節點是指沒有子節點的節點。 示例: 輸入: 1 / 2 3 5 輸出: [“1->2->5”, “1->3”] 解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3 代碼 /*** Definition for a b…

英特爾神經計算棒_如何設置英特爾Movidius神經計算棒

英特爾神經計算棒by Rishal Hurbans由Rishal Hurbans 如何設置英特爾Movidius神經計算棒 (How to set up the Intel Movidius Neural Compute Stick) In 2017 I was approached by Intel to join their Innovator Programme. After a couple interviews I was inducted as an …

linux 腳本中的push,linux shell之pushd、popd和dirs的使用講解

1 問題我們有時候需要保存多個路徑&#xff0c;上下鍵切換不方便&#xff0c;用cd-只能到上個目錄&#xff0c;我們可以用dirs和pushd和popd2 dirs、pushd、popddirs: 這個命令顯示棧里面所有的路徑&#xff0c;一定會包含當前路徑,常用參數如下dirs -v 顯示棧里面的所有路徑和…

為什么我從 Git Flow 開發模式切換到了 Trunk Based 開發模式?

我已經使用 Git Flow 構建我的 Git 分支有幾年了。但是&#xff0c;我遇到了 Git Flow 的一些問題&#xff0c;其中大部分來自長期存在的分支。解決這些問題的方案就是 Trunk Based Development。這是一個非常簡單的技術&#xff0c;也是有效的持續交付的基礎。在這篇文章中&am…

DedeCMS 提示信息! ----------dede_addonarticle

把數據保存到數據庫附加表 dede_addonarticle 時出錯&#xff0c;請把相關信息提交給DedeCms官方。Duplicate entry ’2532′ for key ‘PRIMARY’出現這種情況其實是你的主鍵是不可重復的&#xff0c;現在重復插入值為2532的主鍵了。可以去掉主鍵唯一&#xff0c;或是設成自增…

angular 模塊構建_通過構建全棧應用程序學習Angular 6

angular 模塊構建Angular 6 is out! The new features include better performance, new powerful CLI additions and a new way to inject services.Angular 6出來了&#xff01; 新功能包括更好的性能&#xff0c;新的功能強大的CLI附加功能以及注入服務的新方法。 This tut…

leetcode74. 搜索二維矩陣(二分查找)

編寫一個高效的算法來判斷 m x n 矩陣中&#xff0c;是否存在一個目標值。該矩陣具有如下特性&#xff1a; 每行中的整數從左到右按升序排列。 每行的第一個整數大于前一行的最后一個整數。 示例 1: 輸入: matrix [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] tar…

搭建基于.NetFrameWork的私有nuget服務端及打包項目發布上傳

一、私有Nuget服務端搭建 1.創建一個.NetFramework web項目 2.在nuget管理中 安裝 nuget.server包 3.安裝完成后修改web.config里面的 apikey 和 packagesPath apikey&#xff1a;推送包到nuget服務端 packpage: 上傳上來的包存放的服務器位置 4.發布web項目到IIS中&#xff0c…

linux 網絡配置 阮一峰,Vim 配置入門

Vim 是最重要的編輯器之一&#xff0c;主要有下面幾個優點。可以不使用鼠標&#xff0c;完全用鍵盤操作。系統資源占用小&#xff0c;打開大文件毫無壓力。鍵盤命令變成肌肉記憶以后&#xff0c;操作速度極快。服務器默認都安裝 Vi 或 Vim。Vim 的配置不太容易&#xff0c;它有…

spring 之 property-placeholder 分析

不難知道&#xff0c; property-placeholder 的解析是 PropertyPlaceholderBeanDefinitionParser 完成的&#xff0c; 但是 它僅僅是個parser &#xff0c; 它僅僅是讀取了 location 等配置屬性&#xff0c; 并沒有完成真正的解析&#xff0c;及 注冊。 <context:property-p…

leetcode面試題 10.02. 變位詞組

編寫一種方法&#xff0c;對字符串數組進行排序&#xff0c;將所有變位詞組合在一起。變位詞是指字母相同&#xff0c;但排列不同的字符串。 注意&#xff1a;本題相對原題稍作修改 示例: 輸入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”], 輸出: [ [“ate”,…

hacktoberfest_我第一次參加Hacktoberfest中學到了什么

hacktoberfestImposter syndrome is something we all struggle with to one degree or another. Imposter syndrome is the fear of exposure as a fraud. If you’re anything like me you have felt like your work was not good enough to show. Or you weren’t far along…

--save 和--save-dev的區別

npm install 在安裝 npm 包時&#xff0c;有兩種命令參數可以把它們的信息寫入 package.json 文件&#xff0c;一個是npm install --save另一個是 npm install --save-dev&#xff0c;他們表面上的區別是--save 會把依賴包名稱添加到 package.json 文件 dependencies 鍵下&…

Linux 文件區塊連續嗎,關于Linux文件系統的的簡單理解和認識

關于Linux文件系統的的簡單理解和認識關于文件系統的運作&#xff0c;這與操作系統帶的檔案數據有關。例如Linux操作系統的檔案權限(rwx)與文件屬性(擁有者&#xff0c;群組&#xff0c;時間參數等)。文件系統通常會將這兩部分的數據分別存放在不同的區塊&#xff0c;權限與屬性…