2010-04-11 19 views
8

Đây là vấn đề tôi gặp phải khi cố gắng triển khai trò chơi bằng cách sử dụng công cụ LÖVE, bao gồm box2d với Lua scripting.Lập trình trò chơi vật lý box2d - định hướng đối tượng giống như tháp pháo bằng cách sử dụng các mảnh

Mục tiêu rất đơn giản: Đối tượng giống như tháp pháo (nhìn từ trên cùng, trên môi trường 2D) cần định hướng chính nó để nó trỏ đến mục tiêu.

Tháp pháo nằm trên tọa độ x, y và mục tiêu là trên tx, ty. Chúng ta có thể xem xét rằng x, y được cố định, nhưng tx, ty có xu hướng thay đổi từ một thời điểm này sang một thời điểm khác (tức là chúng sẽ là con trỏ chuột).

Tháp pháo có rôto có thể áp dụng lực quay (mô-men xoắn) vào bất kỳ thời điểm nào, theo chiều kim đồng hồ hoặc ngược chiều kim đồng hồ. Độ lớn của lực đó có giới hạn trên gọi là maxTorque.

Tháp pháo cũng có quán tính quay nhất định, hoạt động cho chuyển động góc cũng giống như cách khối lượng hoạt động cho chuyển động tuyến tính. Không có ma sát nào, vì vậy tháp pháo sẽ tiếp tục quay nếu nó có vận tốc góc.

Tháp pháo có chức năng AI nhỏ đánh giá lại hướng của nó để xác minh rằng nó chỉ đúng hướng và kích hoạt bộ xoay. Điều này xảy ra mỗi dt (~ 60 lần mỗi giây). Có vẻ như sau:

function Turret:update(dt) 
    local x,y = self:getPositon() 
    local tx,ty = self:getTarget() 
    local maxTorque = self:getMaxTorque() -- max force of the turret rotor 
    local inertia = self:getInertia() -- the rotational inertia 
    local w = self:getAngularVelocity() -- current angular velocity of the turret 
    local angle = self:getAngle() -- the angle the turret is facing currently 

    -- the angle of the like that links the turret center with the target 
    local targetAngle = math.atan2(oy-y,ox-x) 

    local differenceAngle = _normalizeAngle(targetAngle - angle) 

    if(differenceAngle <= math.pi) then -- counter-clockwise is the shortest path 
    self:applyTorque(maxTorque) 
    else -- clockwise is the shortest path 
    self:applyTorque(-maxTorque) 
    end 
end 

... không thành công. Hãy để tôi giải thích với hai tình huống minh họa:

  • Tháp pháo "dao động" xung quanh targetAngle.
  • Nếu mục tiêu là "ngay phía sau tháp pháo, chỉ cần một chút đồng hồ", tháp pháo sẽ bắt đầu áp dụng mômen theo chiều kim đồng hồ và tiếp tục áp dụng chúng cho đến khi nó vượt qua góc mục tiêu. Tại thời điểm đó nó sẽ bắt đầu áp dụng mômen theo hướng ngược lại. Nhưng nó sẽ đạt được vận tốc góc quan trọng, vì vậy nó sẽ tiếp tục đi theo chiều kim đồng hồ trong một thời gian ... cho đến khi mục tiêu sẽ là "ngay phía sau, nhưng một chút ngược chiều kim đồng hồ". Và nó sẽ bắt đầu lại. Vì vậy, tháp pháo sẽ dao động hoặc thậm chí đi trong vòng tròn.

Tôi nghĩ tháp pháo của tôi nên bắt đầu áp dụng các mômen xoắn theo hướng ngược lại của đường đi ngắn nhất trước khi đạt đến góc đích (như phanh xe trước khi dừng).

Bằng trực giác, tôi nghĩ tháp pháo nên "bắt đầu áp dụng các mômen xoắn theo hướng ngược lại của đường đi ngắn nhất khi nó gần bằng nửa đường đến mục tiêu đích". Trực giác của tôi nói với tôi rằng nó có liên quan đến vận tốc góc. Và sau đó có một thực tế là các mục tiêu là điện thoại di động - Tôi không biết nếu tôi nên đưa vào tài khoản bằng cách nào đó hoặc chỉ cần bỏ qua nó.

Làm cách nào để tính toán khi nào tháp pháo phải "bắt đầu phanh"?

Trả lời

1

Ok tôi tin rằng tôi đã nhận được giải pháp.

Điều này dựa trên ý tưởng của Beta, nhưng có một số chỉnh sửa cần thiết. Ở đây nó đi:

local twoPi = 2.0 * math.pi -- small optimisation 

-- returns -1, 1 or 0 depending on whether x>0, x<0 or x=0 
function _sign(x) 
    return x>0 and 1 or x<0 and -1 or 0 
end 

-- transforms any angle so it is on the 0-2Pi range 
local _normalizeAngle = function(angle) 
    angle = angle % twoPi 
    return (angle < 0 and (angle + twoPi) or angle) 
end 

function Turret:update(dt) 

    local tx, ty = self:getTargetPosition() 
    local x, y = self:getPosition() 
    local angle = self:getAngle() 
    local maxTorque = self:getMaxTorque() 
    local inertia = self:getInertia() 
    local w = self:getAngularVelocity() 

    local targetAngle = math.atan2(ty-y,tx-x) 

    -- distance I have to cover 
    local differenceAngle = _normalizeAngle(targetAngle - angle) 

    -- distance it will take me to stop 
    local brakingAngle = _normalizeAngle(_sign(w)*2.0*w*w*inertia/maxTorque) 

    local torque = maxTorque 

    -- two of these 3 conditions must be true 
    local a,b,c = differenceAngle > math.pi, brakingAngle > differenceAngle, w > 0 
    if((a and b) or (a and c) or (b and c)) then 
    torque = -torque 
    end 

    self:applyTorque(torque) 
end 

Khái niệm đằng sau điều này rất đơn giản: tôi cần tính toán cần bao nhiêu "góc" tháp pháo để dừng hoàn toàn. Điều đó phụ thuộc vào tốc độ di chuyển của tháp pháo và bao nhiêu mô men xoắn có thể áp dụng cho chính nó. Tóm lại, đó là những gì tôi tính toán với brakingAngle.

Công thức tính góc này hơi khác so với bản Beta. Một người bạn của tôi đã giúp tôi với các vật lý, và tốt, họ dường như đang làm việc. Thêm dấu hiệu của w là ý tưởng của tôi.

Tôi phải triển khai chức năng "bình thường hóa", đặt bất kỳ góc nào trở lại vùng 0-2Pi.

Ban đầu đây là một vướng mắc nếu-else-if-else. Vì các điều kiện rất lặp đi lặp lại, tôi đã sử dụng một số boolean logic để đơn giản hóa thuật toán. Nhược điểm là, ngay cả khi nó hoạt động ok và nó không phải là phức tạp, nó không transpire lý do tại sao nó hoạt động.

Khi mã được thêm một chút nữa, tôi sẽ đăng liên kết tới bản trình diễn tại đây.

Thanks a lot.

EDIT: Làm việc mẫu L isVE hiện có sẵn here. Nội dung quan trọng là bên trong diễn viên/AI.lua (tập tin .love có thể mở bằng bộ nén zip)

0

Bạn có thể tìm phương trình vận tốc góc so với khoảng cách góc cho rôto khi mômen tăng tốc được áp dụng và tìm phương trình tương tự khi mô-men xoắn phanh được áp dụng.

Sau đó sửa đổi phương trình vi phạm sao cho nó intesects trục khoảng cách góc ở góc cần thiết. Với hai phương trình này, bạn có thể tính toán khoảng cách góc mà tại đó chúng giao nhau sẽ cho bạn điểm phá vỡ.

Có thể hoàn toàn sai, tuy nhiên, không được thực hiện như thế này trong một thời gian dài. Có lẽ là một giải pháp đơn giản hơn. Tôi giả định rằng gia tốc không phải là tuyến tính.

1

Điều này có vẻ như vấn đề có thể được giải quyết bằng PID controller. Tôi sử dụng chúng trong công việc của mình để điều khiển đầu ra của lò sưởi để đặt nhiệt độ.

Đối với các thành phần 'P', bạn áp dụng một mô-men xoắn đó là tỷ lệ thuận với sự khác biệt giữa các góc tháp pháo và góc mục tiêu tức là

P = P0 * differenceAngle

Nếu điều này vẫn dao động quá nhiều (nó sẽ một chút) sau đó thêm một 'tôi' thành phần,

integAngle = integAngle + differenceAngle * dt 
I = I0 * integAngle

Nếu đây overshoots quá nhiều sau đó thêm một 'D' hạn

derivAngle = (prevDifferenceAngle - differenceAngle)/dt 
prevDifferenceAngle = differenceAngle 
D = D0 * derivAngle

P0, I0D0 là các hằng số mà bạn có thể điều chỉnh để có được hành vi mà bạn muốn (tức là nhanh như thế nào các tháp pháo phản ứng, vv)

Cũng giống như một mẹo, thường P0>I0>D0

Sử dụng các thuật ngữ này để xác định có bao nhiêu mô-men xoắn được áp dụng tức là

magnitudeAngMomentum = P + I + D

EDIT:

Đây là một ứng dụng được viết bằng cách sử dụng Processing sử dụng PID. Nó thực sự hoạt động tốt mà không có tôi hoặc D. Xem nó hoạt động here


// Demonstration of the use of PID algorithm to 
// simulate a turret finding a target. The mouse pointer is the target 

float dt = 1e-2; 
float turretAngle = 0.0; 
float turretMass = 1; 
// Tune these to get different turret behaviour 
float P0 = 5.0; 
float I0 = 0.0; 
float D0 = 0.0; 
float maxAngMomentum = 1.0; 

void setup() { 
    size(500, 500); 
    frameRate(1/dt); 
} 

void draw() { 
    background(0); 
    translate(width/2, height/2); 

    float angVel, angMomentum, P, I, D, diffAngle, derivDiffAngle; 
    float prevDiffAngle = 0.0; 
    float integDiffAngle = 0.0; 

    // Find the target 
    float targetX = mouseX; 
    float targetY = mouseY; 
    float targetAngle = atan2(targetY - 250, targetX - 250); 

    diffAngle = targetAngle - turretAngle; 
    integDiffAngle = integDiffAngle + diffAngle * dt; 
    derivDiffAngle = (prevDiffAngle - diffAngle)/dt; 

    P = P0 * diffAngle; 
    I = I0 * integDiffAngle; 
    D = D0 * derivDiffAngle; 

    angMomentum = P + I + D; 

    // This is the 'maxTorque' equivelant 
    angMomentum = constrain(angMomentum, -maxAngMomentum, maxAngMomentum); 

    // Ang. Momentum = mass * ang. velocity 
    // ang. velocity = ang. momentum/mass 
    angVel = angMomentum/turretMass; 

    turretAngle = turretAngle + angVel * dt; 

    // Draw the 'turret' 
    rotate(turretAngle); 
    triangle(-20, 10, -20, -10, 20, 0); 

    prevDiffAngle = diffAngle; 
} 
+0

Vấn đề với phương pháp này là nó được thiết kế cho những thứ như hệ thống sưởi, nơi bạn kiểm soát đạo hàm đầu tiên của nhiệt độ; egarcia là mô-men xoắn kiểm soát, đó là mô-men xoắn thứ hai. P sẽ vượt qua wildly vì mục tiêu của nó cho một = 0, không w = 0, tôi không giúp với dao động, D có thể làm việc nhưng nó sẽ làm cho quá trình SLOW. – Beta

+0

Bạn nói đúng, ví dụ tôi đưa ra không trực tiếp đối phó với mô-men xoắn. Tuy nhiên 'maxAngleMomentum' tỉ lệ với' maxTorque' khi ta xem xét ma sát trong cơ chế quay 'tháp pháo' - người ta có thể xem chúng có thể hoán đổi cho nhau khi sử dụng các đơn vị tùy ý. – Brendan

+0

Việc triển khai có vẻ ổn. Ý tưởng "bảo tồn động lượng góc" là thú vị. Tuy nhiên đây không phải là những gì tôi đã yêu cầu- Tôi muốn áp dụng mô-men xoắn, trong khi ở cuối bạn đang thiết lập các góc chính mình. Nhưng 1 để làm cho một bản demo và mã sang trọng. – kikito

2

Nghĩ về phía sau. Tháp pháo phải "bắt đầu phanh" khi nó vừa đủ chỗ để giảm tốc từ vận tốc góc hiện tại đến điểm dừng chết, giống như căn phòng cần tăng tốc từ điểm dừng đến vận tốc góc hiện tại, là

|differenceAngle| = w^2*Inertia/2*MaxTorque. 

Bạn cũng có thể gặp một số sự cố với dao động nhỏ xung quanh đích nếu thời gian bước của bạn quá lớn; mà sẽ đòi hỏi một chút khéo léo hơn, bạn sẽ phải phanh một chút sớm hơn, và nhẹ nhàng hơn. Đừng lo lắng về điều đó cho đến khi bạn nhìn thấy nó.

Điều đó sẽ đủ tốt ngay bây giờ, nhưng có một sự bắt đầu khác có thể khiến bạn phải đi sau: quyết định nên đi đường nào. Đôi khi đi đường dài xung quanh là nhanh hơn, nếu bạn đang đi theo cách đó rồi. Trong trường hợp đó bạn phải quyết định cách nào mất ít thời gian hơn, điều đó không khó, nhưng một lần nữa, băng qua cây cầu đó khi bạn đến đó.

EDIT:
phương trình của tôi là sai, nó phải là Quán tính/2 * maxTorque, không 2 * maxTorque/Quán tính (đó là những gì tôi nhận được cho cố gắng làm đại số ở bàn phím). Tôi đã sửa nó.

Hãy thử điều này:

local torque = maxTorque; 
if(differenceAngle > math.pi) then -- clockwise is the shortest path 
    torque = -torque; 
end 
if(differenceAngle < w*w*Inertia/(2*MaxTorque)) then -- brake 
    torque = -torque; 
end 
self:applyTorque(torque) 
+0

Hi Beta, cảm ơn bạn đã trả lời. Tôi nên nói rằng toán học của tôi không phải là rất mạnh. Tôi phải làm gì với phương trình đó? Một cái gì đó có vẻ là tiềm ẩn cho bạn nhưng tôi chỉ không thể nhìn thấy nó. – kikito

+0

mm Tôi đã đưa ra suy nghĩ này nhiều hơn.Theo những nỗ lực khiêm tốn của tôi, góc = MaxTorque * t * t/quán tính, trong một khoảng thời gian nhất định t. Cách bạn đi từ phương trình đó đến phương trình bạn hiển thị trên ví dụ của bạn đã thoát khỏi tôi. – kikito

+0

Bạn gần như đã nhận được: tăng tốc alpha = maxTorque/quán tính, w = alpha * t, nhưng để tính góc bạn phải sử dụng tốc độ trung bình trong khoảng thời gian, vì vậy góc = (maxTorque * t/Quán tính) * t/2 . Bây giờ lấy w = alpha * t và vuông nó: w * w = alpha * alpha * t * t, và sử dụng nó để loại bỏ t * t: angle = alpha * t * t/2 = alpha * (w * w/alpha * alpha)/2 = w * w/2 * alpha = w * w * Quán tính/2 * maxTorque. – Beta

0

Một phiên bản đơn giản của vấn đề này là khá đơn giản để giải quyết. Giả sử động cơ có mô-men xoắn vô hạn, tức là nó có thể thay đổi vận tốc ngay lập tức. Điều này rõ ràng là không chính xác về thể chất nhưng làm cho vấn đề đơn giản hơn nhiều để giải quyết và cuối cùng không phải là một vấn đề.

Tập trung vào vận tốc góc mục tiêu không phải là góc mục tiêu.

current_angle = "the turrets current angle"; 
target_angle = "the angle the turret should be pointing"; 
dt = "the timestep used for Box2D, usually 1/60"; 
max_omega = "the maximum speed a turret can rotate"; 

theta_delta = target_angle - current_angle; 
normalized_delta = normalize theta_delta between -pi and pi; 
delta_omega = normalized_deta/dt; 
normalized_delta_omega = min(delta_omega, max_omega); 

turret.SetAngularVelocity(normalized_delta_omega); 

Lý do hoạt động này là tháp pháo tự động cố gắng di chuyển chậm hơn khi nó đạt đến góc mục tiêu.

Mô-men xoắn vô hạn được che chắn bởi thực tế rằng tháp pháo không cố gắng đóng khoảng cách ngay lập tức. Thay vào đó, nó cố gắng đóng khoảng cách trong một dấu thời gian. Cũng vì phạm vi của -pi đến pi là khá nhỏ, sự tăng tốc có thể điên rồ không bao giờ thể hiện bản thân. Vận tốc góc tối đa giữ cho vòng quay của tháp pháo trông thực tế.

Tôi chưa bao giờ nghiên cứu phương trình thực để giải quyết mô men xoắn thay vì vận tốc góc, nhưng tôi tưởng tượng nó sẽ trông rất giống với phương trình PID.

+0

Tôi thích ý tưởng của bạn và tôi sẽ ghi nhớ điều đó trong tương lai. Cảm ơn bạn đã chia sẻ nó. Nếu bạn được insterested, tôi quản lý để tìm phương trình và chương trình một ví dụ làm việc. – kikito

Các vấn đề liên quan