Sự cố phát hiện va chạm khi sử dụng AABB


8

Tôi đã triển khai một thói quen phát hiện va chạm đơn giản bằng cách sử dụng AABB giữa sprite trò chơi chính của tôi và các nền tảng khác nhau (Vui lòng xem mã bên dưới). Nó hoạt động rất tốt, nhưng giờ tôi đang giới thiệu lực hấp dẫn để làm cho nhân vật của tôi ngã xuống và nó xuất hiện một số vấn đề với thói quen CD của tôi.

Tôi nghĩ mấu chốt của vấn đề là thói quen CD của tôi di chuyển sprite trở lại dọc theo trục mà nó đã thâm nhập ít nhất vào sprite khác. Vì vậy, nếu nó nằm trong trục Y nhiều hơn X, thì nó sẽ di chuyển nó trở lại dọc theo Trục X.

Tuy nhiên, bây giờ sprite (anh hùng) của tôi hiện đang giảm ở tốc độ 30 pixel mỗi khung hình (Đó là màn hình cao 1504 - Tôi cần nó giảm nhanh như vậy vì tôi muốn thử mô phỏng lực hấp dẫn 'bình thường', bất kỳ tốc độ chậm nào trông cũng kỳ lạ ) Tôi đang nhận được những vấn đề này. Tôi sẽ cố gắng hiển thị những gì đang xảy ra (và những gì tôi nghĩ là gây ra nó - mặc dù tôi không chắc chắn) với một vài hình ảnh: (Mã bên dưới hình ảnh).

Tôi sẽ đánh giá cao một số gợi ý về cách khắc phục vấn đề này.

nhập mô tả hình ảnh ở đây

Để làm rõ, trong hình bên phải ở trên, khi tôi nói vị trí được sửa 'không chính xác' có thể hơi sai lệch. Bản thân mã đang hoạt động chính xác cho cách viết hoặc đặt một cách khác, chính thuật toán nếu hành xử theo cách tôi mong đợi, nhưng tôi cần thay đổi hành vi của nó để ngăn chặn vấn đề này xảy ra, hy vọng điều đó làm rõ ý kiến ​​của tôi trong ảnh .

nhập mô tả hình ảnh ở đây

Mã của tôi

public boolean heroWithPlatforms(){

    //Set Hero center for this frame
    heroCenterX  = hero.xScreen+(hero.quadWidth/2);
    heroCenterY  = hero.yScreen+(hero.quadHeight/2);

    //Iterate through all platforms to check for collisions
    for(x=0;x<platformCoords.length;x+=2){

    //Set platform Center for this iteration
    platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
    platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);

    // the Dif variables are the difference (absolute value)
    // of the center of the two sprites being compared (one for X axis difference
    //and on for the Y axis difference)
    difX = Math.abs(heroCenterX-platformCenterX);
    difY = Math.abs(heroCenterY-platformCenterY);

    //If 1
    //Check the difference between the 2 centers and compare it to the vector (the sum of
    //the two sprite's widths/2.  If it is smaller, then the sprites are pverlapping along
    //the X axis and we can now proceed to check the Y axis 
    if (difX<vecXPlatform){

    //If 2
    //Same as above but for the Y axis, if this is also true, then we have a collision
    if(difY<vecYPlatform){
        //we now know sprites are colliding, so we now need to know exactly by how much
        //penX will hold the value equal to the amount one sprite (hero, in this case)
        //has overlapped with the other sprite (the platform)
        penX = vecXPlatform-difX;
        penY = vecYPlatform-difY;
        //One sprite has penetrated into the other, mostly in the Y axis, so move sprite
        //back on the X Axis
        if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
        //Sprite has penetrated into the other, mostly in the X asis, so move sprite
        //back on the Y Axis
        else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
        return true;
        }//Close 'If' 2
        } //Close 'If' 1
    }
    //Otherwise, no collision

    return false;
    }

Để giải thích, tại sao bạn lại di chuyển các sprite trở lại trên trục khác: //One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Tom 'Blue' Piddock

Xin chào @Blue, vì sprite nên được sửa dọc theo trục xuyên ít nhất, vì vậy nếu sprite đã thâm nhập vào nền tảng nhiều hơn trong Trục X so với Y, thì đó là Y nên được sửa vì nó là ít hơn trong hai.
BungleBonce


Tôi đã đọc câu hỏi đó trước @Anko - nó không cho tôi câu trả lời tôi cần. Do đó tôi đã tự sáng tác câu hỏi của mình (Không thuyết phục được người hỏi đang hỏi chính xác điều tương tự, mặc dù rất khó để chắc chắn) :-)
BungleBonce

Câu trả lời:


1

trong các thử nghiệm của tôi với HTML5 canvas và AABB, tôi đã tìm thấy chính xác những gì bạn đang trải nghiệm. Điều này đã xảy ra khi tôi cố gắng tạo một nền tảng từ các hộp liền kề 32x32 pixel.

Các giải pháp tôi đã thử theo thứ tự sở thích cá nhân của tôi

1 - Phân chia chuyển động theo trục

Trò chơi hiện tại của tôi và tôi nghĩ tôi sẽ tiếp tục trò chơi của mình với nó. Nhưng hãy chắc chắn kiểm tra những gì tôi gọi là Phantom AABB nếu bạn cần một giải pháp nhanh chóng mà không phải sửa đổi nhiều thiết kế hiện tại của bạn.

Thế giới trò chơi của tôi có vật lý rất đơn giản. Không có khái niệm về lực lượng (chưa), chỉ có sự dịch chuyển. Trọng lực là một sự dịch chuyển liên tục xuống. Không tăng tốc (chưa).

Dịch chuyển được áp dụng bởi trục. Và trong này bao gồm giải pháp đầu tiên này.

Mỗi khung trò chơi:

player.moves.y += gravity;
player.moves.x += move_x;

move_x được đặt theo xử lý đầu vào, nếu không nhấn phím mũi tên thì move_x = 0. Các giá trị dưới 0 có nghĩa là trái, nếu không ở bên phải.

Xử lý tất cả các sprite di chuyển khác và quyết định các giá trị của thành phần "di chuyển" của chúng, chúng cũng có thành phần "di chuyển".

Lưu ý: sau khi phát hiện và phản ứng va chạm, thành phần di chuyển phải được đặt thành 0, cả hai thuộc tính x và y, bởi vì dấu tích tiếp theo, trọng lực sẽ được thêm lại. Nếu bạn không thiết lập lại, bạn sẽ kết thúc bằng một động tác. Với hai lần trọng lực mong muốn và trong lần đánh dấu tiếp theo, nó sẽ là ba lần.

Sau đó nhập mã dịch chuyển và áp dụng phát hiện va chạm.

Thành phần di chuyển không thực sự là vị trí hiện tại của sprite. Sprite chưa di chuyển ngay cả khi di chuyển.x hoặc y thay đổi. Thành phần "di chuyển" là một cách để lưu chuyển vị được áp dụng cho vị trí sprite trong phần chính xác của vòng lặp trò chơi.

Trước tiên xử lý trục y (di chuyển.y). Áp dụng di chuyển.y cho sprite.poseition.y. Sau đó áp dụng phương pháp AABB để xem liệu sprite di chuyển đang được xử lý có chồng lấp nền tảng AABB không (bạn đã hiểu cách thực hiện việc này). Nếu nó trùng nhau thì di chuyển sprite trở lại trong trục y bằng cách áp dụng thâm nhập.y. Vâng, trái ngược với câu trả lời khác của tôi, bây giờ phương thức tiến hóa này đã bỏ qua sự thâm nhập.x, cho bước này của quy trình. Và chúng tôi sử dụng thâm nhập.y ngay cả khi nó lớn hơn thâm nhập.x, chúng tôi thậm chí không kiểm tra nó.

Sau đó xử lý trục x (di chuyển.x). Áp dụng di chuyển.x cho sprite.poseition.x. Và làm ngược lại hơn trước. Bạn sẽ di chuyển sprite chỉ trong trục ngang lần này bằng cách áp dụng thâm nhập.x. Như trước đây, bạn không quan tâm nếu thâm nhập.x nhỏ hơn hoặc lớn hơn thâm nhập.y.

Khái niệm ở đây, là nếu sprite không di chuyển và ban đầu không va chạm, thì nó sẽ vẫn như vậy trong khung tiếp theo (sprite.move.x và y bằng 0). Trường hợp đặc biệt khi một đối tượng trò chơi khác dịch chuyển một cách kỳ diệu đến một vị trí chồng lấp sprite bắt đầu được xử lý là điều tôi sẽ xử lý sau.

Khi một sprite bắt đầu di chuyển. Nếu nó chỉ di chuyển theo trục x, thì chúng ta chỉ quan tâm nếu nó xâm nhập một cái gì đó bên trái hoặc bên phải. Vì sprite không di chuyển xuống và chúng tôi biết điều này bởi vì thuộc tính y của thành phần di chuyển bằng 0, chúng tôi thậm chí không kiểm tra giá trị tính toán cho thâm nhập.y, chúng tôi chỉ nhìn thấy ở thâm nhập.x. Nếu sự thâm nhập tồn tại trong trục chuyển động, chúng ta sẽ áp dụng hiệu chỉnh, nếu không, chúng ta chỉ cần để sprite di chuyển.

Điều gì về chuyển vị đường chéo, không nhất thiết phải ở một góc 45 độ?

Hệ thống đề xuất có thể xử lý nó. Bạn chỉ cần xử lý từng trục riêng biệt. Trong quá trình mô phỏng của bạn. Các lực khác nhau được áp dụng cho sprite. Ngay cả khi động cơ của bạn chưa hiểu lực lượng. Các lực này chuyển thành một sự dịch chuyển tùy ý trong mặt phẳng 2D, sự dịch chuyển này là một vectơ 2D theo bất kỳ hướng nào và có độ dài bất kỳ. Từ vectơ này, bạn có thể trích xuất trục y và trục x.

Phải làm gì nếu vectơ dịch chuyển quá lớn?

Bạn không muốn điều này:

|
|
|
|
|
|
|
|_ _ _ _ _ _ _ _ _

Khi các bước di chuyển mới của chúng tôi áp dụng logic xử lý một trục trước rồi đến trục kia, một sự dịch chuyển lớn có thể kết thúc được nhìn thấy bởi mã va chạm như chữ L lớn, điều này sẽ không phát hiện chính xác các va chạm với các vật thể giao nhau với vectơ dịch chuyển của bạn.

Giải pháp là phân chia các chuyển vị lớn theo các bước nhỏ, lý tưởng nhất là chiều rộng hoặc chiều cao sprite của bạn, hoặc một nửa chiều rộng hoặc chiều cao sprite của bạn. Để có được một cái gì đó như thế này:

|_
  |_
    |_
      |_
        |_
          |_
            |_
              |_

Điều gì về dịch chuyển tức thời các sprite?

Nếu bạn đại diện cho dịch chuyển tức thời như một sự dịch chuyển lớn, sử dụng thành phần di chuyển tương tự như một di chuyển bình thường, thì không có vấn đề gì. Nếu bạn không, thì bạn phải nghĩ cách đánh dấu sprite dịch chuyển tức thời được xử lý bằng mã va chạm (thêm vào danh sách dành riêng cho mục đích này?), Trong mã phản hồi, bạn có thể thay thế trực tiếp sprite đứng trong cách khi dịch chuyển tức thời xảy ra.

2 - Phantom AABB

Có AABB thứ cấp cho mỗi sprite bị ảnh hưởng bởi trọng lực bắt đầu từ đáy sprite, có cùng chiều rộng của AABB sprite và chiều cao 1 pixel.

Ý tưởng ở đây là AABB ảo này luôn luôn va chạm với nền tảng bên dưới sprite. Chỉ khi AABB ảo này không chồng lấp bất cứ thứ gì trọng lực mới được phép áp dụng cho sprite.

Bạn không cần tính toán vectơ thâm nhập cho AABB ảo và nền tảng. Bạn chỉ quan tâm đến một thử nghiệm va chạm đơn giản, nếu nó chồng lên một nền tảng, trọng lực không thể được áp dụng. Sprite của bạn có thể có một thuộc tính boolean cho biết nếu nó nằm trên một nền tảng hoặc trong không khí. Bạn đang ở trên không khi AABB ảo của bạn không va chạm với một nền tảng bên dưới trình phát.

Điều này:

player.position.y += gravity;

Trở thành một cái gì đó như thế này:

if (player.onGround == false) player.position.y += gravity;

Rất đơn giản và đáng tin cậy.

Bạn để lại thực hiện AABB của bạn như là.

AABB ảo có thể có một vòng lặp chuyên dụng xử lý chúng, chỉ kiểm tra các va chạm đơn giản và không lãng phí thời gian để tính toán vectơ thâm nhập. Vòng lặp này phải trước mã va chạm và phản hồi thông thường. Bạn có thể áp dụng trọng lực trong vòng lặp này, đối với những sprite trên không khí áp dụng trọng lực. Nếu AABB ảo trong chính nó là một lớp hoặc cấu trúc, nó có thể có một tham chiếu đến sprite mà nó thuộc về.

Điều này rất giống với giải pháp được đề xuất khác nơi bạn kiểm tra xem người chơi của bạn có nhảy không. Tôi đưa ra một cách có thể phát hiện nếu người chơi có chân trên một nền tảng.


Tôi thích giải pháp Phantom AABB ở đây. Tôi đã áp dụng một cái gì đó tương tự để giải quyết vấn đề. (Nhưng không sử dụng AABB ảo) - Tôi muốn tìm hiểu thêm về phương pháp nội bộ của bạn ở trên, nhưng tôi hơi bối rối với nó. Thành phần Moves.x (và .y) về cơ bản là lượng mà sprite sẽ được di chuyển (nếu nó được phép di chuyển)? Ngoài ra, tôi không rõ làm thế nào nó sẽ giúp với tình huống của tôi trực tiếp. Sẽ rất biết ơn nếu bạn có thể chỉnh sửa câu trả lời của mình để hiển thị một đồ họa như thế nào nó có thể giúp với tình huống của tôi (như trong hình của tôi ở trên) - cảm ơn!
BungleBonce

Về thành phần di chuyển. Nó đại diện cho sự dịch chuyển trong tương lai, vâng, nhưng bạn không kiểm tra xem bạn có cho phép di chuyển hay không, bạn thực sự áp dụng chuyển động, theo trục, đầu tiên là y, sau đó x, và sau đó xem bạn có va chạm với thứ gì không, nếu bạn sử dụng sự thâm nhập vector để di chuyển trở lại. Tại sao lưu dịch chuyển cho xử lý sau này khác với thực tế áp dụng dịch chuyển khi có đầu vào của người dùng để xử lý hoặc điều gì đó đã xảy ra trong thế giới trò chơi? Tôi sẽ cố gắng đưa điều này vào hình ảnh khi tôi chỉnh sửa câu trả lời của mình. Trong khi đó hãy xem nếu có người khác đi kèm với một sự thay thế.
Hatoru Hansou

Ah OK - Tôi nghĩ tôi hiểu những gì bạn đang nói bây giờ. Vì vậy, thay vì di chuyển x & y và sau đó kiểm tra va chạm, với phương thức của bạn, bạn di chuyển X trước, sau đó kiểm tra va chạm, nếu nó va chạm thì hãy sửa dọc theo trục X. Sau đó di chuyển Y và kiểm tra va chạm, nếu có va chạm, sau đó di chuyển dọc theo trục X? Bằng cách này chúng ta luôn biết trục chính xác của tác động? Tôi đã hiểu đúng chưa?! Cảm ơn!
BungleBonce

Cảnh báo: bạn đã viết "Sau đó di chuyển Y và kiểm tra va chạm, nếu có va chạm, sau đó di chuyển dọc theo trục X?" Trục Y của nó. Có lẽ là một lỗi đánh máy. Vâng, đó là cách tiếp cận hiện tại của tôi. Đối với các chuyển vị rất lớn, nhiều hơn chiều rộng hoặc chiều cao sprite của bạn, bạn cần chia theo các bước nhỏ hơn và đó là ý tôi muốn nói với phép so sánh L. Coi chừng không áp dụng chuyển vị lớn trong trục y và sau đó là x lớn, bạn cần chia cả hai bước nhỏ và xen kẽ để có hành vi chính xác. Một cái gì đó chỉ quan tâm nếu chuyển vị lớn được cho phép trong trò chơi của bạn. Trong tôi, tôi biết chúng sẽ xảy ra. Vì vậy, tôi đã chuẩn bị cho nó.
Hatoru Hansou

Rực rỡ @HatoruHansou, tôi chưa bao giờ nghĩ sẽ làm mọi thứ theo cách này, tôi chắc chắn sẽ thử nó - cảm ơn rất nhiều vì sự giúp đỡ của bạn, tôi sẽ đánh dấu câu trả lời của bạn là được chấp nhận. :-)
BungleBonce

5

Tôi sẽ kết hợp các ô nền tảng thành một thực thể nền tảng duy nhất.

Ví dụ: giả sử bạn lấy 3 ô từ hình ảnh và kết hợp chúng thành một thực thể duy nhất, thực thể của bạn sẽ có một hộp va chạm gồm:

Left   = box1.left
Right  = box3.right
Top    = box1.top
Bottom = box1.bottom

Sử dụng điều đó để va chạm sẽ cung cấp cho bạn y là trục xuyên thấu ít nhất trong hầu hết nền tảng trong khi vẫn cho phép bạn có các ô cụ thể trong nền tảng.


Xin chào @UnderscoreZero - cảm ơn, đây là một cách để đi, điều duy nhất là tôi hiện đang lưu trữ các ô của mình trong 3 mảng riêng biệt, một cho các ô cạnh trái, trên cho các ô ở giữa và một cho các ô cạnh phải - tôi đoán tôi có thể kết hợp tất cả chúng thành một mảng cho mục đích của CD, chỉ không chắc làm thế nào tôi có thể viết mã để làm điều đó.
BungleBonce

Một lựa chọn khác mà bạn có thể thử là rất nhiều động cơ vật lý sử dụng là một khi vật thể đã hạ cánh trên một mặt phẳng, chúng vô hiệu hóa trọng lực cho vật thể để ngăn nó cố gắng rơi qua. Một khi vật thể nhận đủ lực để kéo nó ra khỏi mặt phẳng hoặc nó rơi ra khỏi mặt phẳng, chúng kích hoạt lại trọng lực và thực hiện vật lý như bình thường.
UnderscoreZero

0

Các vấn đề như thế này là phổ biến với các phương pháp phát hiện va chạm mới.

Tôi không biết quá nhiều về thiết lập va chạm hiện tại của bạn, nhưng đây là cách nó sẽ diễn ra:

Trước hết, hãy thực hiện các biến sau:

  1. Tạo vận tốc X và Y
  2. Tạo một trọng lực
  3. Thực hiện một bước nhảy
  4. Thực hiện một cú nhảy

Lần thứ hai, tạo một bộ đếm thời gian để tính toán delta để ảnh hưởng đến vận tốc của vật thể.

Thứ ba, làm điều này:

  1. Kiểm tra xem người chơi có nhấn nút nhảy không và không nhảy, nếu vậy hãy thêm jumpSpeed ​​vào vận tốc Y.
  2. Trừ trọng lực từ vận tốc Y mỗi lần cập nhật để mô phỏng trọng lực
  3. Nếu chiều cao y + của người chơi nhỏ hơn hoặc bằng y của nền tảng, hãy đặt vận tốc Y thành 0 và đặt Y của người chơi lên chiều cao của y + của người chơi.

Nếu bạn làm điều này, sẽ không có bất kỳ vấn đề. Hi vọng điêu nay co ich!


Xin chào @ user1870398, cảm ơn, nhưng trò chơi của tôi thậm chí không nhảy vào lúc này, câu hỏi liên quan đến các vấn đề phát sinh thông qua việc sử dụng phương pháp 'trục xuyên ít nhất' để sửa vị trí của sprite. Vấn đề là lực hấp dẫn đang kéo nhân vật của tôi đi sâu hơn vào các nền tảng của tôi dọc theo Trục Y so với sự chồng chéo trên Trục X, do đó, thói quen của tôi (đúng như vậy đối với loại thuật toán này) là điều chỉnh trục X, do đó tôi không thể di chuyển dọc theo nền tảng (vì thói quen CD nhìn thấy các ô riêng lẻ và xử lý chúng như vậy mặc dù chúng được trình bày dưới dạng một nền tảng).
BungleBonce

Tôi nhận được vấn đề của bạn. Chỉ là bạn không điều chỉnh đúng chuyển động của mình. Bạn cần một biến isJumping . Nếu bạn nhấn nút nhảy và không nhảy, bạn đặt nó thành true, nếu bạn chạm đất, đặt nó thành false, di chuyển Y của người chơi đến đúng vị trí ( platform.y + player.height ) và đặt isJumping thành false. Tiếp theo, ngay sau khi kiểm tra những thứ đó, hãy làm if (left_key) player.velocity.x - = (player.isJumping? Player.speed: player.speed - player.groundFriction) . Tôi biết bạn chưa thực hiện bước nhảy nào nên đơn giản là luôn có isJumping được đặt thành false.
LiquidFeline

Tôi không nhảy nhưng nếu một biến 'ifJumping' luôn được đặt thành false, thì điều này có gì khác khi không có nó? (nói trong các trò chơi không có nhảy) Tôi chỉ hơi bối rối về việc một boolean 'đang nhảy' có thể giúp gì trong tình huống của tôi - sẽ rất biết ơn nếu bạn có thể giải thích thêm một chút - cảm ơn!
BungleBonce

Nó không cần thiết. Chỉ tốt hơn để thực hiện nó trong khi bạn vẫn đang lập trình nó. Dù sao, lý do cho isJumping là để tổ chức mã của bạn và ngăn ngừa sự cố bằng cách chỉ thêm trọng lực trong khi người chơi đang nhảy. Chỉ cần làm những gì tôi đề nghị. Phương pháp này là những gì hầu hết mọi người sử dụng. Ngay cả Minecraft cũng sử dụng nó! Đây chỉ là cách trọng lực được mô phỏng hiệu quả! :)
LiquidFeline
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.