Phép ngoại suy phá vỡ phát hiện va chạm


10

Trước khi áp dụng phép ngoại suy cho chuyển động của sprite của tôi, va chạm của tôi đã hoạt động hoàn hảo. Tuy nhiên, sau khi áp dụng phép ngoại suy cho chuyển động của sprite của tôi (để giải quyết mọi thứ), sự va chạm không còn hoạt động nữa.

Đây là cách mọi thứ hoạt động trước khi ngoại suy:

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

Tuy nhiên, sau khi tôi thực hiện phép ngoại suy của mình, thói quen va chạm bị phá vỡ. Tôi giả sử điều này là do nó hoạt động theo tọa độ mới được tạo ra bởi thói quen ngoại suy (nằm trong lệnh gọi kết xuất của tôi).

Sau khi tôi áp dụng phép ngoại suy của mình

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

Làm thế nào để sửa hành vi này?

Tôi đã thử đặt thêm một kiểm tra va chạm ngay sau khi ngoại suy - điều này dường như làm sáng tỏ rất nhiều vấn đề nhưng tôi đã loại trừ điều này vì đưa logic vào kết xuất của tôi là điều không thể.

Tôi cũng đã thử tạo một bản sao của vị trí spitesX, ngoại suy nó và vẽ bằng cách sử dụng nó chứ không phải bản gốc, do đó để nguyên bản gốc cho logic để chọn - đây có vẻ là một lựa chọn tốt hơn, nhưng nó vẫn tạo ra một số hiệu ứng kỳ lạ khi va chạm với tường. Tôi khá chắc chắn rằng đây cũng không phải là cách chính xác để giải quyết vấn đề này.

Tôi đã tìm thấy một vài câu hỏi tương tự ở đây nhưng câu trả lời không giúp được tôi.

Đây là mã ngoại suy của tôi:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

Áp dụng phép ngoại suy

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

Biên tập

Như tôi đã đề cập ở trên, tôi đã cố gắng tạo một bản sao tọa độ của sprite để vẽ với .... đây có vấn đề riêng.

Đầu tiên, bất kể việc sao chép là gì, khi sprite đang di chuyển, nó rất mượt mà, khi dừng lại, nó hơi lắc lư sang trái / phải - vì nó vẫn ngoại suy vị trí của nó dựa trên thời gian. Đây có phải là hành vi bình thường và chúng ta có thể 'tắt nó' khi sprite dừng lại không?

Tôi đã thử có cờ cho trái / phải và chỉ ngoại suy nếu một trong hai được bật. Tôi cũng đã thử sao chép vị trí cuối cùng và hiện tại để xem có sự khác biệt nào không. Tuy nhiên, theo như sự va chạm, những điều này không giúp được gì.

Nếu người dùng đang nhấn nói, nút bên phải và sprite đang di chuyển sang phải, khi nó chạm vào tường, nếu người dùng tiếp tục giữ nút bên phải xuống, sprite sẽ tiếp tục hoạt động ở bên phải, trong khi bị chặn bởi bức tường ( do đó không thực sự di chuyển), tuy nhiên vì cờ bên phải vẫn được đặt và cũng vì thói quen va chạm liên tục di chuyển sprite ra khỏi tường, nên nó vẫn xuất hiện mã (không phải trình phát) mà sprite vẫn đang di chuyển, và do đó ngoại suy tiếp tục. Vì vậy, những gì người chơi sẽ thấy, là 'tĩnh' (vâng, nó hoạt hình, nhưng nó không thực sự di chuyển trên màn hình), và cứ sau đó nó rung lắc dữ dội khi ngoại suy cố gắng thực hiện điều đó ..... .. Hy vọng điều này giúp đỡ


1
Điều này sẽ mất một số tiêu hóa để hiểu đầy đủ ('nội suy' dường như có hàng tá ý nghĩa khác nhau và nó không hoàn toàn rõ ràng ngay từ cái nhìn đầu tiên của bạn ở đây), nhưng bản năng đầu tiên của tôi là 'bạn không nên làm để ảnh hưởng đến bạn vị trí của đối tượng trong thói quen kết xuất của bạn '. Trình kết xuất của bạn sẽ vẽ đối tượng của bạn tại vị trí đã cho của đối tượng và thao tác với đối tượng đó có một công thức cho sự cố, vì nó vốn đã kết hợp trình kết xuất với vật lý của trò chơi. Trong một thế giới lý tưởng, trình kết xuất của bạn sẽ có thể đưa con trỏ const vào các đối tượng trò chơi.
Steven Stadnicki

Hi @StevenStadnicki, cảm ơn rất nhiều bình luận của bạn, có vô số ví dụ cho thấy một giá trị nội suy được thông qua vào các renderer, vui lòng xem này: mysecretroom.com/www/programming-and-software/... đó là từ mà tôi thích nghi của tôi mã. Hiểu biết hạn chế của tôi là điều này sẽ nội suy vị trí giữa các bản cập nhật cuối cùng và tiếp theo dựa trên lượng thời gian kể từ lần cập nhật cuối cùng - tôi đồng ý rằng đó là một cơn ác mộng! Tôi sẽ biết ơn nếu bạn có thể đề xuất và thay thế rằng tôi sẽ làm việc dễ dàng hơn - chúc mừng :-)
BungleBonce

6
Chà, tôi sẽ nói 'chỉ vì bạn có thể tìm mã cho thứ gì đó không làm cho nó trở thành một cách thực hành tốt nhất'. :-) Trong trường hợp này, tôi nghi ngờ rằng trang bạn đã liên kết sử dụng giá trị nội suy để tìm ra nơi hiển thị các đối tượng của nó, nhưng nó không thực sự cập nhật vị trí đối tượng với chúng; bạn cũng có thể làm điều đó, chỉ bằng cách có một vị trí vẽ cụ thể được tính cho mọi khung hình, nhưng giữ vị trí đó tách biệt với vị trí 'vật lý' thực tế của đối tượng.
Steven Stadnicki

Xin chào @StevenStadnicki, như được nêu trong câu hỏi của tôi (đoạn bắt đầu "Tôi cũng đã thử tạo một bản sao"), tôi thực sự đã cố gắng sử dụng vị trí 'vẽ chỉ' :-) Bạn có thể đề xuất một phương pháp nội suy trong đó tôi không cần điều chỉnh vị trí của sprite trong thói quen kết xuất? Cảm ơn!
BungleBonce

Nhìn vào mã của bạn, có vẻ như bạn đang thực hiện phép ngoại suy thay vì nội suy.
Durza007

Câu trả lời:


1

Tôi chưa thể đăng bình luận, vì vậy tôi sẽ đăng bài này dưới dạng câu trả lời.

Nếu tôi hiểu vấn đề một cách chính xác, nó sẽ diễn ra như sau:

  1. đầu tiên bạn có một vụ va chạm
  2. sau đó vị trí của đối tượng được điều chỉnh (có lẽ là do thói quen phát hiện va chạm)
  3. vị trí cập nhật của đối tượng được gửi đến chức năng kết xuất
  4. chức năng kết xuất sau đó cập nhật vị trí của đối tượng bằng phép ngoại suy
  5. vị trí của đối tượng ngoại suy phá vỡ thói quen phát hiện va chạm

Tôi có thể nghĩ ra 3 giải pháp khả thi. Tôi sẽ liệt kê chúng theo thứ tự mong muốn nhất là ít nhất IMHO.

  1. Di chuyển phép ngoại suy ra khỏi chức năng kết xuất. Ngoại suy vị trí của đối tượng và sau đó kiểm tra va chạm.
  2. hoặc nếu bạn muốn giữ phép ngoại suy trong chức năng kết xuất, hãy đặt cờ để biểu thị một vụ va chạm đã xảy ra. Phát hiện va chạm đúng vị trí của đối tượng như bạn đã làm, nhưng trước khi bạn tính giá trị ngoại suy, hãy kiểm tra cờ va chạm trước. Vì đối tượng đã ở nơi cần đến, nên không cần phải di chuyển nó.
  3. Khả năng cuối cùng, mà đối với tôi là giải quyết nhiều vấn đề hơn là khắc phục sẽ là bù đắp quá mức với phát hiện va chạm. Sau khi va chạm, di chuyển vật thể ra khỏi tường, để sau khi ngoại suy, vật thể trở lại tường.

Mã ví dụ cho # 2.

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

Tôi nghĩ rằng # 2 có thể là cách nhanh nhất và dễ nhất để bắt đầu chạy, mặc dù # 1 dường như là một giải pháp chính xác hơn về mặt logic. Tùy thuộc vào cách bạn xử lý giải pháp thời gian delta # 1 của bạn có thể bị phá vỡ theo cách tương tự bởi một delta lớn, trong trường hợp đó bạn có thể phải sử dụng cả # 1 và # 2 cùng nhau.

EDIT: Tôi đã hiểu nhầm mã của bạn trước đó. Các vòng lặp có nghĩa là hiển thị nhanh nhất có thể và cập nhật tại một khoảng thời gian đã đặt. Đây là lý do tại sao bạn sẽ nội suy vị trí sprite, để xử lý trường hợp bạn đang vẽ nhiều hơn là cập nhật. Tuy nhiên, nếu vòng lặp tụt lại phía sau, thì bạn sẽ thăm dò ý kiến ​​cập nhật cho đến khi bạn bị bắt kịp hoặc bạn bỏ qua số lần rút khung tối đa.

Điều đó đang được nói, vấn đề duy nhất là đối tượng đang di chuyển sau một vụ va chạm. Nếu có va chạm, đối tượng nên dừng di chuyển theo hướng đó. Vì vậy, nếu có xung đột, hãy đặt tốc độ của nó thành 0. Điều này sẽ ngăn chức năng kết xuất không di chuyển đối tượng thêm nữa.


Xin chào @Aholio Tôi đã thử tùy chọn 2 và nó hoạt động nhưng gây ra một vài trục trặc có thể tôi đã làm sai, tôi sẽ xem lại điều đó. Tuy nhiên tôi rất quan tâm đến tùy chọn 1 mặc dù tôi không thể tìm thấy thông tin nào về cách thực hiện việc này. Làm thế nào tôi có thể ngoại suy nói trong thói quen logic của tôi? BTW đồng bằng của tôi được cố định là 1/60 hoặc 0,01667 (thay vào đó là con số tôi sử dụng để tích hợp mặc dù lượng thời gian mỗi lần lặp của trò chơi của tôi rõ ràng có thể thay đổi tùy thuộc vào những gì đang diễn ra nhưng không bao giờ thực sự mất nhiều hơn 0,01667 ) vì vậy bất kỳ trợ giúp về điều đó sẽ được cảm ơn tuyệt vời.
BungleBonce

Có lẽ tôi không hoàn toàn hiểu những gì bạn đang làm. Một điều có vẻ hơi kỳ lạ đối với tôi là bạn không chỉ thực hiện các chuyển động vị trí trong chức năng vẽ của mình; bạn cũng đang tính toán thời gian Điều này được thực hiện cho mọi đối tượng? Thứ tự đúng phải là: tính toán thời gian được thực hiện trong mã vòng lặp trò chơi. Vòng lặp trò chơi chuyển delta vào chức năng cập nhật (dt). Cập nhật (dt) sẽ cập nhật tất cả các đối tượng trò chơi. Nó sẽ xử lý bất kỳ chuyển động, ngoại suy và phát hiện va chạm. Sau khi update () quay trở lại vòng lặp trò chơi, sau đó nó gọi hàm render () để vẽ tất cả các đối tượng trò chơi được cập nhật mới.
Aholio

Hmmmmm hiện tại chức năng cập nhật của tôi làm mọi thứ. Có phải phong trào (theo chuyển động tôi có nghĩa là tính toán vị trí mới của các họa tiết - điều này được tính từ thời gian delta của tôi). Điều duy nhất tôi đang làm trong chức năng kết xuất của mình (ngoài thực tế là vẽ) là ngoại suy vị trí 'mới', nhưng tất nhiên điều này sử dụng thời gian vì nó phải tính đến thời gian từ lần cập nhật logic cuối cùng cho cuộc gọi kết xuất. Dù sao đó cũng là sự hiểu biết của tôi :-) Bạn sẽ làm điều này như thế nào? Tôi không hiểu cách sử dụng phép ngoại suy trong bản cập nhật logic. Cảm ơn!
BungleBonce

Cập nhật câu trả lời của tôi. Hy vọng điều đó sẽ giúp
Aholio

Cảm ơn, hãy xem trong Q ban đầu của tôi "khi nó dừng, nó hơi lắc lư sang trái / phải" - vì vậy ngay cả khi tôi dừng sprite của mình, phép ngoại suy vẫn giữ cho sprite 'di chuyển' (lắc lư) - điều này xảy ra vì tôi không sử dụng Các vị trí cũ và hiện tại của sprite để thực hiện phép ngoại suy của tôi, do đó ngay cả khi sprite là tĩnh, nó vẫn có hiệu ứng lắc lư dựa trên giá trị 'ngoại suy' được thực hiện trong mỗi lần lặp khung. Tôi thậm chí đã thử nói 'nếu vị trí cũ và hiện tại giống nhau thì đừng ngoại suy' nhưng điều này dường như vẫn không hoạt động, tôi sẽ quay lại và xem xét lại!
BungleBonce

0

Có vẻ như bạn cần tách biệt hoàn toàn kết xuất và cập nhật vật lý. Thông thường mô phỏng cơ bản sẽ chạy ở các bước thời gian riêng biệt và tần số sẽ không bao giờ thay đổi. Ví dụ: bạn có thể mô phỏng chuyển động của quả bóng của mình cứ sau 1/60 giây và đó là nó.

Để cho phép một biến số khung hình, mã kết xuất phải hoạt động trên một tần số thay đổi, nhưng bất kỳ mô phỏng nào vẫn phải ở một bước thời gian cố định. Điều này cho phép đồ họa đọc bộ nhớ từ mô phỏng dưới dạng chỉ đọc và cho phép bạn thiết lập phép nội suy thay vì ngoại suy.

Vì phép ngoại suy đang cố gắng dự đoán nơi sẽ có giá trị trong tương lai, những thay đổi đột ngột trong chuyển động có thể mang lại cho bạn những lỗi ngoại suy rất lớn. Thay vào đó, tốt hơn là hiển thị cảnh của bạn về một khung phía sau mô phỏng và nội suy giữa các vị trí đã biết rời rạc.

Nếu bạn muốn xem một số chi tiết thực hiện, tôi đã viết một phần ngắn về chủ đề này trong một bài viết ở đây . Vui lòng xem phần "Dấu thời gian".

Đây là mã psuedo quan trọng từ bài viết:

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

Các RenderGamechức năng được quan tâm nhất. Ý tưởng là sử dụng phép nội suy giữa các vị trí mô phỏng rời rạc. Mã kết xuất có thể tạo các bản sao của dữ liệu chỉ đọc mô phỏng và sử dụng giá trị nội suy tạm thời để kết xuất. Điều này sẽ cung cấp cho bạn chuyển động rất trơn tru mà không có bất kỳ vấn đề ngớ ngẩn như những gì bạn dường như đang có!

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.