Sự cố va chạm AABB 2D Platformer


51

http://dl.dropbox.com/u/3724424/Programming/Gifs/game6.gif

Tôi có một vấn đề với độ phân giải va chạm AABB.


Tôi giải quyết giao điểm AABB bằng cách giải quyết trục X trước, sau đó đến trục Y. Điều này được thực hiện để ngăn chặn lỗi này: http://i.stack.imgur.com/NLg4j.png


Phương thức hiện tại hoạt động tốt khi một đối tượng di chuyển vào trình phát và người chơi phải được đẩy theo chiều ngang. Như bạn có thể thấy trong .gif, các gai ngang đẩy người chơi chính xác.


Tuy nhiên, khi các gai dọc di chuyển vào trình phát, trục X vẫn được giải quyết trước tiên. Điều này làm cho "sử dụng gai như một thang máy" là không thể.

Khi người chơi di chuyển vào các gai thẳng đứng (bị ảnh hưởng bởi trọng lực, rơi vào chúng), anh ta bị đẩy vào trục Y, vì không có sự trùng lặp nào trên trục X để bắt đầu.


Một cái gì đó tôi đã thử là phương pháp được mô tả trong câu trả lời đầu tiên của liên kết này: Phát hiện va chạm đối tượng hình chữ nhật 2D

Tuy nhiên, các gai và các vật thể chuyển động di chuyển bằng cách thay đổi vị trí của chúng, không phải là vận tốc và tôi không tính toán vị trí dự đoán tiếp theo của chúng cho đến khi phương thức Update () của chúng được gọi. Không cần phải nói giải pháp này cũng không hiệu quả. :(


Tôi cần giải quyết xung đột AABB theo cách mà cả hai trường hợp được mô tả ở trên đều hoạt động như dự định.

Đây là mã nguồn va chạm hiện tại của tôi: http://pastebin.com/MiCi3nA1

Tôi thực sự biết ơn nếu ai đó có thể xem xét vấn đề này, vì lỗi này đã xuất hiện trong động cơ ngay từ đầu và tôi đã đấu tranh để tìm một giải pháp tốt, mà không thành công. Điều này thực sự khiến tôi mất nhiều đêm để xem mã va chạm và ngăn tôi đến "phần thú vị" và mã hóa logic trò chơi :(


Tôi đã thử triển khai hệ thống va chạm giống như trong bản demo nền tảng XNA AppHub (bằng cách sao chép hầu hết các nội dung). Tuy nhiên, lỗi "nhảy" xảy ra trong trò chơi của tôi, trong khi nó không xảy ra trong bản demo AppHub. [lỗi nhảy: http://i.stack.imgur.com/NLg4j.png ]

Để nhảy tôi kiểm tra xem người chơi có "onGround" không, sau đó thêm -5 vào Velocity.Y.

Vì Velocity.X của người chơi cao hơn Velocity.Y (tham khảo bảng thứ tư trong sơ đồ), onGround được đặt thành đúng khi không nên và do đó cho phép người chơi nhảy lên giữa không trung.

Tôi tin rằng điều này không xảy ra trong bản demo AppHub vì Velocity.X của người chơi sẽ không bao giờ cao hơn Velocity.Y, nhưng tôi có thể bị nhầm.

Tôi đã giải quyết điều này trước bằng cách giải quyết trên trục X trước, sau đó trên trục Y. Nhưng điều đó làm tăng sự va chạm với các gai như tôi đã nêu ở trên.


5
Thực sự không có gì sai. Phương pháp tôi đang sử dụng đẩy trên trục X trước, sau đó trên trục Y. Đó là lý do tại sao người chơi bị đẩy theo chiều ngang ngay cả trên các gai dọc. Tôi cần tìm một giải pháp tránh "vấn đề nhảy" và đẩy người chơi vào trục nông (trục có độ xuyên thấu ít nhất), nhưng tôi không thể tìm thấy.
Vittorio Romeo

1
Bạn có thể phát hiện khuôn mặt nào của vật cản mà người chơi đang chạm vào và giải quyết khuôn mặt đó
Ioachim

4
@ Johnohn Hobbs, đọc câu hỏi. Vee biết chính xác mã của anh ta đang làm gì, nhưng không biết cách giải quyết một vấn đề nào đó. Bước qua mã sẽ không giúp anh ta trong tình huống này.
Tấn

4
@Maik @Jonathan: Không có gì là "sai" với chương trình - anh ấy hiểu chính xác vị trí và lý do tại sao thuật toán của anh ấy không làm những gì anh ấy muốn. Anh ta chỉ không biết cách thay đổi thuật toán để làm những gì anh ta muốn. Vì vậy, trình gỡ lỗi không hữu ích trong trường hợp này.
BlueRaja - Daniel Pflughoeft

1
@AttackingHobo @BlueRaja Tôi đồng ý và tôi đã xóa nhận xét của mình. Đó là tôi chỉ đi đến kết luận về những gì đang xảy ra. Tôi thực sự xin lỗi vì đã khiến bạn phải giải thích điều đó và vì đã nói sai ít nhất một người. Thành thật mà nói, bạn có thể tin tưởng vào tôi để thực sự tiếp thu câu hỏi trước khi tôi để lại bất kỳ câu trả lời nào vào lần tới.
doppelgreener

Câu trả lời:


9

OK, tôi đã tìm ra lý do tại sao bản demo nền tảng XNA AppHub không có lỗi "nhảy": bản demo kiểm tra các ô va chạm từ trên xuống dưới . Khi chống lại một "bức tường", người chơi có thể chồng chéo nhiều ô. Thứ tự giải quyết rất quan trọng vì giải quyết một vụ va chạm cũng có thể giải quyết các va chạm khác (nhưng theo một hướng khác). Các onGroundbất động sản chỉ được đặt khi va chạm đã được giải quyết bằng cách đẩy các cầu thủ trên trục y. Độ phân giải này sẽ không xảy ra nếu các độ phân giải trước đó đẩy người chơi xuống và / hoặc theo chiều ngang.

Tôi đã có thể tái tạo lỗi "nhảy" trong bản demo XNA bằng cách thay đổi dòng này:

for (int y = topTile; y <= bottomTile; ++y)

đến đây:

for (int y = bottomTile; y >= topTile; --y)

(Tôi cũng đã điều chỉnh một số hằng số liên quan đến vật lý, nhưng điều này không thành vấn đề.)

Có lẽ việc sắp xếp bodiesToChecktheo trục y trước khi giải quyết các va chạm trong trò chơi của bạn sẽ khắc phục lỗi "nhảy". Tôi đề nghị giải quyết vụ va chạm trên trục thâm nhập "nông", như bản demo XNA thực hiện và Trevor gợi ý . Cũng lưu ý rằng trình phát demo XNA cao gấp đôi so với các ô có khả năng va chạm, làm cho nhiều trường hợp va chạm có nhiều khả năng hơn.


Điều này nghe có vẻ thú vị ... Bạn có nghĩ rằng việc sắp xếp mọi thứ theo tọa độ Y trước khi phát hiện va chạm có thể giải quyết tất cả các vấn đề của tôi không? Có bắt được không?
Vittorio Romeo

Aaaand tôi tìm thấy "bắt". Sử dụng phương pháp này, lỗi nhảy được sửa, nhưng nếu tôi đặt Velocity.Y thành 0 sau khi chạm trần, nó sẽ xảy ra lần nữa.
Vittorio Romeo

@Vee: đặt Position = nextPositionngay bên trong forvòng lặp, nếu không, độ phân giải va chạm không mong muốn (cài đặt onGround) vẫn xảy ra. Người chơi nên được đẩy thẳng đứng xuống (và không bao giờ lên) khi chạm trần do đó onGroundkhông bao giờ nên được đặt. Đây là cách bản demo XNA thực hiện và tôi không thể sửa lỗi "trần" ở đó.
Leftium

pastebin.com/TLrQPeBU - đây là mã của tôi: Tôi thực sự làm việc trên chính Vị trí, mà không sử dụng biến "nextPocation". Nó sẽ hoạt động như trong bản demo XNA, nhưng nếu tôi giữ dòng đặt Velocity.Y thành 0 (dòng 57) thì không xảy ra lỗi nhảy. Nếu tôi loại bỏ nó, người chơi sẽ nổi khi nhảy lên trần nhà. Điều này có thể được sửa chữa?
Vittorio Romeo

@Vee: mã của bạn không hiển thị cách onGroundđặt, vì vậy tôi không thể điều tra lý do tại sao nhảy được cho phép không chính xác. Bản demo XNA cập nhật thuộc tính tương tự của nó isOnGroundbên trong HandleCollisions(). Ngoài ra, sau khi cài đặt Velocity.Yvề 0, tại sao trọng lực không bắt đầu di chuyển người chơi xuống, một lần nữa? Tôi đoán là onGroundtài sản được thiết lập không đúng. Hãy xem cách cập nhật bản demo XNA previousBottomisOnGround( IsOnGround).
Leftium

9

Giải pháp đơn giản nhất cho bạn sẽ là kiểm tra cả hai hướng va chạm với mọi vật thể trên thế giới trước khi giải quyết bất kỳ va chạm nào, và chỉ giải quyết nhỏ hơn trong hai "va chạm hỗn hợp". Điều này có nghĩa là bạn giải quyết bằng số tiền nhỏ nhất có thể, thay vì luôn luôn giải quyết x trước hoặc luôn giải quyết y trước.

Mã của bạn sẽ trông giống như thế này:

// This loop repeats, until our object has been fully pushed outside of all
// collision objects
while ( StillCollidingWithSomething(object) )
{
  float xDistanceToResolve = XDistanceToMoveToResolveCollisions( object );
  float yDistanceToResolve = YDistanceToMoveToResolveCollisions( object );
  bool xIsColliding = (xDistanceToResolve != 0.f);

  // if we aren't colliding on x (not possible for normal solid collision 
  // shapes, but can happen for unidirectional collision objects, such as 
  // platforms which can be jumped up through, but support the player from 
  // above), or if a correction along y would simply require a smaller move 
  // than one along x, then resolve our collision by moving along y.

  if ( !xIsColliding || fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) )
  {
    object->Move( 0.f, yDistanceToResolve );
  }
  else // otherwise, resolve the collision by moving along x
  {
    object->Move( xDistanceToResolve, 0.f );
  }
}

sửa đổi lớn : Từ việc đọc bình luận về các câu trả lời khác, tôi nghĩ cuối cùng tôi đã nhận thấy một giả định không có căn cứ, điều này sẽ khiến cách tiếp cận này không hiệu quả (và điều này giải thích tại sao tôi không thể hiểu được những vấn đề mà một số - nhưng không phải tất cả - mọi người đã thấy với cách tiếp cận này). Để giải thích, đây là một số mã giả khác, cho thấy rõ hơn những chức năng mà tôi đã tham chiếu trước đây được cho là thực sự đang làm:

bool StillCollidingWithSomething( MovingObject object )
{
  // loop over every collision object in the world.  (Implementation detail:
  // don't test 'object' against itself!)
  for( int i = 0; i < collisionObjectCount; i++ )
  {
    // if the moving object overlaps any collision object in the world, then
    // it's colliding
    if ( Overlaps( collisionObject[i], object ) )
      return true;
  }
  return false;
}

float XDistanceToMoveToResolveCollisions( MovingObject object )
{
  // check how far we'd have to move left or right to stop colliding with anything
  // return whichever move is smaller
  float moveOutLeft = FindDistanceToEmptySpaceAlongNegativeX(object->GetPosition());
  float moveOutRight = FindDistanceToEmptySpaceAlongX(object->GetPosition());
  float bestMoveOut = min( fabs(moveOutLeft), fabs(moveOutRight) );

  return minimumMove;
}

float FindDistanceToEmptySpaceAlongX( Vector2D position )
{
  Vector2D cursor = position;
  bool colliding = true;
  // until we stop colliding...
  while ( colliding )
  {
    colliding = false;
    // loop over all collision objects...
    for( int i = 0; i < collisionObjectCount; i++ )
    {
      // and if we hit an object...
      if ( Overlaps( collisionObject[i], cursor ) )
      {
        // move outside of the object, and repeat.
        cursor.x = collisionObject[i].rightSide;
        colliding = true;

        // break back to the 'while' loop, to re-test collisions with
        // our new cursor position
        break;
      }
    }
  }
  // return how far we had to move, to reach empty space
  return cursor.x - position.x;  
}

Đây không phải là thử nghiệm "cho mỗi cặp đối tượng"; nó không hoạt động bằng cách kiểm tra và phân giải đối tượng chuyển động theo từng ô của bản đồ thế giới (cách tiếp cận đó sẽ không bao giờ hoạt động đáng tin cậy và thất bại theo những cách ngày càng thảm khốc khi kích thước gạch giảm). Thay vào đó, nó đang thử nghiệm đồng thời vật thể di chuyển chống lại mọi vật thể trên bản đồ thế giới, và sau đó giải quyết dựa trên các va chạm với toàn bản đồ thế giới.

Đây là cách bạn đảm bảo rằng (ví dụ) các ô tường riêng lẻ trong một bức tường không bao giờ đẩy người chơi lên xuống giữa hai ô liền kề, dẫn đến việc người chơi bị mắc kẹt trong một không gian không tồn tại 'giữa' chúng; khoảng cách giải quyết va chạm luôn được tính toán đến không gian trống trên thế giới, không chỉ đơn thuần là ranh giới của một ô duy nhất mà sau đó có thể có một ô rắn khác ở trên nó.


1
i.stack.imgur.com/NLg4j.png - điều này xảy ra nếu tôi sử dụng phương pháp trên.
Vittorio Romeo

1
Có thể tôi không hiểu chính xác mã của bạn, nhưng hãy để tôi giải thích vấn đề trong sơ đồ: vì trình phát nhanh hơn trên trục X, độ thâm nhập X> hơn thâm nhập Y. Va chạm chọn trục Y (vì độ xuyên thấu nhỏ hơn) và đẩy người chơi theo chiều dọc. Điều này không xảy ra nếu người chơi đủ chậm để thâm nhập Y luôn> hơn thâm nhập X.
Vittorio Romeo

1
@Vee Cửa sổ nào của sơ đồ mà bạn đang đề cập, ở đây, khi đưa ra một hành vi không chính xác khi sử dụng phương pháp này? Tôi giả sử khung 5, trong đó người chơi rõ ràng thâm nhập ít hơn vào X so với Y, điều này sẽ dẫn đến việc bị đẩy ra dọc theo trục X - đó là hành vi đúng. Bạn chỉ nhận được hành vi xấu nếu bạn chọn trục cho độ phân giải dựa trên vận tốc (như trong hình ảnh của bạn), và không dựa trên sự thâm nhập thực tế (như tôi đã đề xuất).
Trevor Powell

1
@Trevor: Ah, tôi hiểu những gì bạn đang nói. Trong trường hợp đó, bạn có thể chỉ cần làm cho nóif(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
BlueRaja - Danny Pflughoeft

1
@BlueRaja Điểm tuyệt vời. Tôi đã chỉnh sửa câu trả lời của mình để sử dụng ít điều kiện hơn, như bạn đề xuất. Cảm ơn!
Trevor Powell

6

Biên tập

Tôi đã cải thiện nó

Có vẻ như điều quan trọng tôi đã thay đổi là phân loại các giao lộ.

Giao lộ là:

  • Trần nhà
  • Chạm đất (đẩy ra khỏi sàn)
  • Va chạm giữa không trung
  • Va chạm tường

và tôi giải quyết chúng theo thứ tự đó

Xác định một vụ va chạm trên mặt đất khi người chơi ở ít nhất 1/4 trên ô

Vì vậy, đây là một vụ va chạm trên mặt đất và người chơi ( màu xanh ) sẽ ngồi trên đỉnh của viên gạch ( màu đen ) va chạm mặt đất

Nhưng đây KHÔNG phải là một vụ va chạm trên mặt đất và người chơi sẽ "trượt" ở bên phải của viên gạch khi anh ta đáp xuống nó không gonig là một người chơi va chạm mặt đất sẽ trượt qua

Theo phương pháp này, người chơi sẽ không còn bị bắt ở hai bên tường


Tôi đã thử phương pháp của bạn (xem phần triển khai của tôi: pastebin.com/HarnNnnp ) nhưng tôi không biết đặt Velocity của người chơi ở đâu 0. Tôi đã thử đặt nó thành 0 nếu encrY <= 0 nhưng điều đó cho phép người chơi dừng giữa không trung trong khi trượt dọc theo một bức tường và cũng cho phép anh ta nhảy tường liên tục.
Vittorio Romeo

Tuy nhiên, điều này hoạt động chính xác khi người chơi tương tác với các gai (như được hiển thị trong .gif). Tôi thực sự sẽ đánh giá cao nếu bạn có thể giúp tôi giải quyết vấn đề nhảy tường (cũng bị kẹt trong tường). Hãy nhớ rằng các bức tường của tôi được làm bằng một số gạch AABB.
Vittorio Romeo

Xin lỗi vì đã đăng một bình luận khác, nhưng sau khi sao chép mã của bạn trong một dự án hoàn toàn mới, thay đổi sp thành 5 và làm cho gạch xuất hiện trong hình dạng tường, lỗi nhảy xảy ra (tham khảo sơ đồ này: i.stack.imgur.com /NLg4j.png)
Vittorio Romeo

Ok, thử ngay bây giờ.
bobobobo

+1 cho mẫu mã làm việc (mặc dù thiếu các khối "tăng đột biến" theo chiều ngang :)
Leftium

3

Forenote:
Công cụ của tôi sử dụng lớp phát hiện va chạm để kiểm tra va chạm với tất cả các đối tượng theo thời gian thực, chỉ khi một đối tượng di chuyển và chỉ trong các ô vuông lưới mà nó hiện đang chiếm giữ.

Giải pháp của tôi:

Khi tôi chạy đua với các vấn đề như thế này trong nền tảng 2d của mình, tôi đã làm một cái gì đó như sau:

- (động cơ phát hiện các va chạm có thể xảy ra, nhanh chóng)
- (trò chơi thông báo cho động cơ để kiểm tra các va chạm chính xác cho đối tượng cụ thể) [trả về vectơ của đối tượng *]
- (đối tượng nhìn vào độ sâu thâm nhập, cũng như vị trí trước đó so với vị trí trước đó của đối tượng khác, để xác định phía nào trượt ra khỏi)
- (đối tượng di chuyển và tính trung bình vận tốc của nó với vận tốc của đối tượng (nếu đối tượng đang di chuyển))


"(đối tượng nhìn vào độ sâu thâm nhập, cũng như vị trí trước đó so với vị trí trước đó của đối tượng khác, để xác định bên nào sẽ trượt ra)" đây là phần tôi quan tâm. Bạn có thể giải thích không? Liệu nó có thành công trong tình huống được hiển thị bởi .gif và sơ đồ không?
Vittorio Romeo

Tôi vẫn chưa tìm thấy một tình huống (ngoài vận tốc cao) mà phương pháp này không hoạt động.
ultifinitus

Âm thanh tốt, nhưng bạn thực sự có thể giải thích làm thế nào để bạn giải quyết thâm nhập? Bạn có giải quyết luôn trên trục nhỏ nhất? Bạn có kiểm tra phía của vụ va chạm?
Vittorio Romeo

Không, như một vấn đề thực tế, trục nhỏ hơn không phải là một cách tốt (chính) để đi. IMHO. Tôi thường giải quyết dựa trên mặt nào trước đây nằm ngoài mặt của đối tượng khác, đó là mặt phổ biến nhất. Khi thất bại, sau đó tôi kiểm tra dựa trên vận tốc và độ sâu.
ultifinitus

2

Trong các trò chơi video, tôi đã lập trình cách tiếp cận là có chức năng cho biết vị trí của bạn có hợp lệ hay không, tức là. bool ValidPos (int x, int y, int wi, int he);

Hàm kiểm tra hộp giới hạn (x, y, wi, he) đối với tất cả các hình dạng trong trò chơi và trả về false nếu có bất kỳ giao điểm nào.

Nếu bạn muốn di chuyển, nói sang phải, chọn vị trí người chơi, thêm 4 vào x và kiểm tra xem vị trí đó có hợp lệ không, nếu không, hãy kiểm tra với + 3, + 2, v.v.

Nếu bạn cũng cần phải có lực hấp dẫn, bạn cần một biến phát triển miễn là bạn không chạm đất (chạm đất: ValidPos (x, y + 1, wi, he) == true, y là dương ở dưới). nếu bạn có thể di chuyển khoảng cách đó (ví dụ: RationalPos (x, y + trọng lực, wi, anh ấy) trả về đúng) bạn đang rơi, đôi khi hữu ích khi bạn không thể điều khiển nhân vật của mình khi ngã.

Bây giờ, vấn đề của bạn là bạn có các đối tượng trong thế giới của mình di chuyển nên trước hết bạn cần kiểm tra xem vị trí cũ của bạn có còn hợp lệ không!

Nếu không, bạn cần tìm một vị trí. Nếu các đối tượng ingame không thể di chuyển nhanh hơn nói 2 pixel cho mỗi vòng quay trò chơi, bạn sẽ cần kiểm tra xem vị trí (x, y-1) có hợp lệ không, sau đó (x, y-2) sau đó (x + 1, y), v.v. v.v ... phải kiểm tra toàn bộ khoảng cách giữa (x-2, y-2) đến (x + 2, y + 2). Nếu không có vị trí hợp lệ thì có nghĩa là bạn đã bị 'nghiền nát'.

HTH

Valmond


Tôi đã từng sử dụng phương pháp này trở lại trong những ngày Game Maker của tôi. Tôi có thể nghĩ ra cách tăng tốc / cải thiện nó bằng các tìm kiếm tốt hơn (giả sử tìm kiếm nhị phân trên không gian bạn đã đi, mặc dù tùy thuộc vào khoảng cách bạn đã đi và hình học, nó có thể chậm hơn), nhưng nhược điểm chính của phương pháp này là rằng thay vì thực hiện một kiểm tra đối với tất cả các va chạm có thể xảy ra, bạn đang làm bất cứ điều gì thay đổi vị trí của bạn là kiểm tra.
Jeff

"Bạn đang làm bất cứ điều gì thay đổi vị trí của bạn là kiểm tra" Xin lỗi nhưng chính xác thì điều đó có nghĩa gì? Tất nhiên BTW bạn sẽ sử dụng các kỹ thuật phân vùng không gian (BSP, Octree, quadtree, Tilemap, v.v.) để tăng tốc trò chơi nhưng dù sao bạn sẽ luôn cần phải làm điều đó (nếu bản đồ lớn), tuy nhiên câu hỏi không phải là về điều đó về thuật toán được sử dụng để di chuyển (chính xác) người chơi.
Valmond

@Valmond Tôi nghĩ rằng @Jeff có nghĩa là nếu người chơi của bạn di chuyển trái 10 pixel, bạn có thể có tới 10 phát hiện va chạm khác nhau.
Jonathan Connell

Nếu đó là ý nghĩa của anh ta thì anh ta đã đúng và nó phải như vậy (ai có thể nói nếu chúng ta dừng lại ở +6 chứ không phải là +7?). Máy tính rất nhanh, tôi đã sử dụng nó trên S40 (~ 200mhz và kvm không quá nhanh) và nó hoạt động như một bùa mê. Bạn luôn có thể cố gắng tối ưu hóa thuật toán ban đầu nhưng điều đó sẽ luôn cung cấp cho bạn các trường hợp góc giống như trường hợp mà OP có.
Valmond

Đó chính xác là những gì tôi muốn nói
Jeff

2

Tôi có một vài câu hỏi trước khi tôi bắt đầu trả lời điều này. Đầu tiên, trong lỗi ban đầu mà bạn bị mắc kẹt trong các bức tường, những viên gạch đó ở các ô riêng lẻ bên trái có phải là một viên gạch lớn không? Và nếu là họ, người chơi có bị kẹt giữa họ không? Nếu có cho cả hai câu hỏi đó, chỉ cần đảm bảo vị trí mới của bạn là hợp lệ . Điều đó có nghĩa là bạn sẽ phải kiểm tra xem có va chạm vào nơi bạn bảo người chơi di chuyển hay không. Vì vậy, giải quyết chuyển vị tối thiểu như được mô tả dưới đây, và sau đó di chuyển người chơi của bạn dựa trên điều đó chỉ khi anh ta có thểdi chuyển đến đó Hầu như quá dưới mũi: P Điều này thực sự sẽ giới thiệu một lỗi khác, mà tôi gọi là "trường hợp góc". Về cơ bản là về các góc (như phía dưới bên trái nơi các gai ngang xuất hiện trong .gif của bạn, nhưng nếu không có gai) sẽ không giải quyết được xung đột, bởi vì nó sẽ nghĩ rằng không có độ phân giải nào bạn tạo ra dẫn đến vị trí hợp lệ . Để giải quyết vấn đề này, chỉ cần giữ một bool xem liệu vụ va chạm đã được giải quyết chưa, cũng như một danh sách tất cả các độ phân giải thâm nhập tối thiểu. Sau đó, nếu xung đột chưa được giải quyết, hãy lặp lại mọi độ phân giải bạn đã tạo và theo dõi độ phân giải X tối đa và Y tối đa (mức tối đa không phải đến từ cùng độ phân giải). Sau đó giải quyết va chạm trên các mức tối đa đó. Điều này dường như để giải quyết tất cả các vấn đề của bạn cũng như những vấn đề tôi gặp phải.

    List<Vector2> collisions = new List<Vector2>();
        bool resolved = false;
        foreach (Platform p in testPlat)
        {
            Vector2 dif = p.resolveCollision(player.getCollisionMask());

            RectangleF newPos = player.getCollisionMask();

            newPos.X -= dif.X;
            newPos.Y -= dif.Y;


            if (!PlatformCollision(newPos)) //This checks if there's a collision (ie if we're entering an invalid space)
            {
                if (dif.X != 0)
                    player.velocity.X = 0; //Do whatever you want here, I like to stop my player on collisions
                if (dif.Y != 0)
                    player.velocity.Y = 0;

                player.MoveY(-dif.Y);
                player.MoveX(-dif.X);
                resolved = true;
            }
            collisions.Add(dif);
        }

        if (!resolved)
        {
            Vector2 max = Vector2.Zero;

            foreach (Vector2 v in collisions)
            {
                if (Math.Abs(v.X) > Math.Abs(max.X))
                {
                    max.X = v.X;
                }
                if (Math.Abs(v.Y) > Math.Abs(max.Y))
                {
                    max.Y = v.Y;
                }
            }

            player.MoveY(-max.Y);

            if (max.Y != 0)
                player.velocity.Y = 0;
            player.MoveX(-max.X);

            if (max.X != 0)
                player.velocity.X = 0;
        }

Một câu hỏi khác, là các gai bạn hiển thị một ô, hoặc các ô riêng lẻ? Nếu chúng là các ô mỏng riêng lẻ, bạn có thể phải sử dụng một cách tiếp cận khác cho các ô ngang và dọc so với những gì tôi mô tả bên dưới. Nhưng nếu toàn bộ gạch này sẽ hoạt động.


Được rồi, về cơ bản đây là những gì @Trevor Powell đã mô tả. Vì bạn chỉ sử dụng AABB, tất cả những gì bạn phải làm là tìm xem hình chữ nhật này thâm nhập vào hình kia bao nhiêu. Điều này sẽ cung cấp cho bạn một số lượng trong trục X và Y. Chọn tối thiểu trong số hai và di chuyển đối tượng va chạm của bạn dọc theo trục đó. Đó là tất cả những gì bạn cần để giải quyết vụ va chạm AABB. Bạn KHÔNG BAO GIỜ cần phải di chuyển dọc theo nhiều trục trong một vụ va chạm như vậy, vì vậy bạn không bao giờ nên bối rối về việc nên di chuyển cái nào trước, vì bạn sẽ chỉ di chuyển tối thiểu.

Phần mềm Metanet có một hướng dẫn cổ điển về cách tiếp cận ở đây . Nó cũng đi vào các hình dạng khác là tốt.

Đây là một hàm XNA tôi đã thực hiện để tìm vectơ chồng chéo của hai hình chữ nhật:

    public Point resolveCollision(Rectangle otherRect)
    {
        if (!isCollision(otherRect))
        {
            return Point.Zero;
        }

        int minOtherX = otherRect.X;
        int maxOtherX = otherRect.X + otherRect.Width;

        int minMyX = collisionMask.X;
        int maxMyX = collisionMask.X + collisionMask.Width;

        int minOtherY = otherRect.Y;
        int maxOtherY = otherRect.Y + otherRect.Height;

        int minMyY = collisionMask.Y;
        int maxMyY = collisionMask.Y + collisionMask.Height;

        int dx, dy;

        if (maxOtherX - minMyX < maxMyX - minOtherX)
        {
            dx = (maxOtherX - minMyX);
        }
        else
        {
            dx = -(maxMyX - minOtherX);
        }

        if (maxOtherY - minMyY < maxMyY - minOtherY)
        {
            dy = (maxOtherY - minMyY);
        }
        else
        {
            dy = -(maxMyY - minOtherY);
        }

        if (Math.Abs(dx) < Math.Abs(dy))
            return new Point(dx, 0);
        else
            return new Point(0, dy);
    }

(Tôi hy vọng làm theo đơn giản, vì tôi chắc chắn có nhiều cách tốt hơn để thực hiện nó ...)

isCollision (Hình chữ nhật) theo nghĩa đen chỉ là một cuộc gọi đến Hình chữ nhật của XNA.Intersects (Hình chữ nhật).

Tôi đã thử nghiệm điều này với các nền tảng di chuyển và nó dường như hoạt động tốt. Tôi sẽ thực hiện thêm một số thử nghiệm tương tự như .gif của bạn để đảm bảo và báo cáo lại nếu nó không hoạt động.



Tôi hơi hoài nghi về nhận xét đó mà tôi đã đưa ra về nó không hoạt động với các nền tảng mỏng. Tôi không thể nghĩ ra một lý do tại sao nó sẽ không. Ngoài ra nếu bạn muốn có nguồn, tôi có thể tải lên một dự án XNA cho bạn
Jeff

Vâng, tôi chỉ đang thử nghiệm điều này thêm một lần nữa và nó quay trở lại cùng một vấn đề của bạn về việc bị mắc kẹt trong tường. Đó là bởi vì hai gạch xếp thẳng hàng với nhau, nó bảo bạn di chuyển lên vì cái thấp hơn, rồi ra ngoài (ngay trong trường hợp của bạn) cho cái trên, làm giảm hiệu quả chuyển động của trọng lực nếu vận tốc y của bạn đủ chậm . Tôi sẽ phải xem xét một giải pháp cho vấn đề này, tôi dường như nhớ có vấn đề này trước đây.
Jeff

Được rồi, tôi đã nghĩ ra một cách cực kỳ khó khăn để khắc phục vấn đề đầu tiên của bạn. Giải pháp liên quan đến việc tìm kiếm sự thâm nhập tối thiểu cho mỗi AABB, chỉ giải quyết trên một với X lớn nhất, sau đó vượt qua lần thứ hai chỉ giải quyết trên Y lớn nhất. Nó trông tệ khi bạn bị nghiền nát, nhưng nâng lên và bạn có thể bị đẩy theo chiều ngang, và vấn đề gắn bó ban đầu của bạn đã biến mất. Đó là hackey, và tôi không biết nó sẽ dịch sang các hình dạng khác như thế nào. Một khả năng khác có thể là hàn các hình học cảm động, nhưng tôi thậm chí đã không thử điều đó
Jeff

Tường được làm bằng gạch 16x16. Gai là một gạch 16x16. Nếu tôi giải quyết trên trục tối thiểu, lỗi nhảy xảy ra (nhìn vào sơ đồ). Giải pháp "cực kỳ hacky" của bạn có hoạt động với tình huống được hiển thị trong .gif không?
Vittorio Romeo

Đúng, làm việc trong tôi. Kích thước nhân vật của bạn là gì?
Jeff

1

Thật đơn giản.

Viết lại các gai. Đó là lỗi của gai.

Va chạm của bạn nên xảy ra trong các đơn vị riêng biệt, hữu hình. Vấn đề theo như tôi có thể thấy là:

1. Player is outside spikes
2. Spikes move into intersection position with the player
3. Player resolves incorrectly

Vấn đề là ở bước 2 chứ không phải 3 !!

Nếu bạn đang cố gắng để mọi thứ trở nên vững chắc, bạn không nên để các vật phẩm trượt vào nhau như thế. Một khi bạn ở vị trí giao nhau, nếu bạn mất vị trí của mình, vấn đề sẽ trở nên khó giải quyết hơn.

Các gai nên kiểm tra lý tưởng sự tồn tại của người chơi và khi họ di chuyển, họ nên đẩy anh ta khi cần thiết.

Một cách dễ dàng để đạt được điều này là cho Người chơi có moveXvà các moveYchức năng hiểu được cảnh quan và sẽ đẩy người chơi theo một vùng đồng bằng nhất định hoặc xa nhất có thể mà không gặp phải chướng ngại vật .

Thông thường chúng sẽ được gọi bởi vòng lặp sự kiện. Tuy nhiên, chúng cũng có thể được gọi bởi các đối tượng để đẩy người chơi.

function spikes going up:

    move spikes up

    if intersecting player:
        d = player bottom edge + spikes top edge

        player.moveY(-d)
    end if

end function

Rõ ràng người chơi vẫn cần phải phản ứng với các gai nếu anh ta đi vào chúng .

Quy tắc bao trùm là, sau khi bất kỳ đối tượng nào di chuyển, nó sẽ kết thúc ở một vị trí không có giao điểm. Bằng cách này, không thể có được những trục trặc bạn đang thấy.


trong trường hợp cực đoan khi moveYkhông thể loại bỏ giao lộ (vì một bức tường khác đang cản đường), bạn có thể kiểm tra lại giao lộ và thử di chuyển hoặc chỉ cần giết anh ta.
Chris Burt-Brown
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.