碼農干貨系列【2】--由關節(Joint)說到割繩子(cut the rope)
2012-06-08 08:03 【當耐特】 閱讀(5020) 評論(14) 收藏 舉報簡介
關節是相互連結且互相約束的物體,常見于各類物理引擎當中。關節的運用非常廣泛,例如人體模擬、動物行走模擬、器材、繩子、機關、鏈橋等都可以靈活利用關節去模擬。
普通的關節分兩種,一種是有固定點,一種沒有固定點。本文分別對兩種關節進行計算并且輸出圖片進行模擬。
關節
關節通常用下面這種表達方式:
(function (window) {
var Joint = function (segLength, segCount, isFixed, startPoint) {
this.segLength = segLength;
this.segCount = segCount;
this.isFixed = isFixed;
this.startPoint = startPoint;
this.points = [];
for (var i = 0; i < this.segCount; i++) {
this.points.push(new Vector2(this.startPoint.x, this.startPoint.y + i * this.segLength));
}
}
window.Joint = Joint;
} (window))
其中:
segLength表示關節每一段的長度(這里假定關節每一段是相等的)
segCount表示關節個數(包括起點和終點)
isFixed表示關節是否有固定點(如果isFixed為true,假設startPoint為固定點)
startPoint表示關節的起點(這里假定關節的初始狀態是筆直向下的)
points表示關節上所有的支點(包括起點和終點)
這里需要了解的是 ,在完整的關節表示當中,為了更好的模擬現實世界當中的物體,關節還會加上一個角度區間限制,即關節的最大張開角度和最小的角度。本文的關節不加此限制,任其360度無障礙旋轉。
圖形化輸出
這里使用Easeljs輸出關節的圖像。先引用相關的腳本:
<script src="script/Vector2.js" type="text/javascript"></script> <script src="script/joint.js" type="text/javascript"></script> <script src="script/easel.js" type="text/javascript"></script>
定義一個擁有2個關節段,每段長度為60的關節。完整代碼如下所示:
var joint = new Joint(60,4,false,new Vector2(200,100));
var canvas;
var stage;
var shape;
(function (){
canvas = document.getElementById("testCanvas");
stage = new Stage(canvas);
shape = new Shape();
stage.addChild(shape);
drawjoint(joint);
}())
function drawjoint(joint) {
shape.graphics.clear();
shape.graphics.ss(14, 'round', 'round');
for (var i = 0; i < joint.points.length - 1; i++) {
if (i % 2 === 0) {
shape.graphics.beginStroke("green");
}
else {
shape.graphics.beginStroke("red");
}
shape.graphics.mt(joint.points[i].x, joint.points[i].y).lt(joint.points[i + 1].x, joint.points[i + 1].y);
}
shape.graphics.endStroke();
stage.update();
}
與鼠標交互
要讓關節繞著對應的支點動起來,這里讓關節的終點跟隨鼠標的位置移動。
在方法中加入:
canvas.onmousemove = canvasMouseMoveHandler;
所以,當鼠標移動的時候,需要實時的更新關節的位置。如下圖所示:
這里需要注意的兩點是:
上圖描述的是一次微小的拉動,真正要呈現如圖所示的前后狀態,其實已經經歷的很多次位置更新
上圖反向延長線經過初始點是不準確的,準確的位置是初始點靠右一段距離(取決于兩條線段的合力方向,但這不影響關節的模擬)
添加鼠標處理的方法:
function canvasMouseMoveHandler(e) {
var r = canvas.getBoundingClientRect();
joint.points[joint.points.length - 1] = new Vector2(e.clientX - r.left, e.clientY - r.top);
joint.updatePointsPosition(joint.points[joint.points.length - 1], joint.points.length - 1);
drawjoint(joint);
}
在分析完具體的過程之后,利用遞歸的思路依次更新所有的點。更新方法接收兩個參數:一個是更新的點、一個是該點的index,當index為1的時候退出遞歸。
var p = Joint.prototype;
p.updatePointsPosition = function (point, index) {
var tempV = this.points[index - 1].sub(point).setLength(this.segLength);
this.points[index - 1] = point.add(tempV);
if (index > 1) {
this.updatePointsPosition(this.points[index - 1], index - 1);
}
}
其中var tempV = this.points[index - 1].sub(point).setLength(this.segLength);是計算支點的偏移量,Vector2.setLength是經過normalize(轉換為該向量的單位向量) 再multiplyScalar(設置長度)。如下代碼所示:
setLength: function (l) {
return this.normalize().multiplyScalar(l);
},
固定起始點
上面呈現的是沒有固定點的關節,那么如果擁有固定點,該怎么更新關節上所有點的位置呢?需要做的僅僅是校正startPoint(啟始點、固定點)的位置。
var p = Joint.prototype;
p.updatePointsPosition = function (point, index) {
var tempV = this.points[index - 1].sub(point).setLength(this.segLength);
this.points[index - 1] = point.add(tempV);
if (index > 1) {
this.updatePointsPosition(this.points[index - 1], index - 1);
} else {
if (this.isFixed) {
var v = this.points[0].sub(this.startPoint);
for (var i = 0; i < this.points.length; i++) {
this.points[i].subSelf(v);
}
}
}
}
當遞歸到最后,如果該關節是有固定點的,校正所有關節點的位置。效果如下所示:
思考??
這樣子呢?
var joint = new Joint(2,140,true,new Vector2(200,100));
一共140個點,點與點之間距離為2,再加上點顏色區間變化。
它是割繩子(cut the rope)中繩子的表達方式嗎?這樣表示繩子會有什么問題出現?
那么請繼續關注我后續的干貨~~~~~



浙公網安備 33010602011771號