Làm cách nào để ngăn XNA tạm dừng Cập nhật trong khi cửa sổ đang được thay đổi kích thước / di chuyển?


15

XNA dừng cuộc gọi UpdateDrawtrong khi cửa sổ trò chơi đang được thay đổi kích thước hoặc di chuyển.

Tại sao? Có cách nào để ngăn chặn hành vi này?

(Nó khiến mã mạng của tôi không đồng bộ hóa, vì các thông điệp mạng không được bơm.)

Câu trả lời:


20

Đúng. Nó liên quan đến một lượng nhỏ gây rối với nội bộ của XNA. Tôi đã có một minh chứng về cách khắc phục trong nửa sau của video này .

Lý lịch:

Lý do điều này xảy ra là vì XNA tạm dừng đồng hồ trò chơi của nó khi phần Gamebên dưới Formbị thay đổi kích thước (hoặc di chuyển, các sự kiện là như nhau). Điều này, đến lượt nó, là bởi vì nó đang điều khiển vòng lặp trò chơi của nó từ đó Application.Idle. Khi một cửa sổ được thay đổi kích thước, Windows sẽ thực hiện một số nội dung vòng lặp tin nhắn win32 điên rồ ngăn không cho Idlebắn.

Vì vậy, nếu Gamekhông tạm dừng đồng hồ, sau khi thay đổi kích thước, nó sẽ tích lũy được một lượng thời gian khổng lồ mà sau đó nó sẽ phải cập nhật trong một đợt duy nhất - rõ ràng là không mong muốn.

Cách khắc phục:

Có một chuỗi các sự kiện dài trong XNA (nếu bạn sử dụng Game, điều này không áp dụng cho các cửa sổ tùy chỉnh) về cơ bản kết nối sự kiện thay đổi kích thước của phương thức cơ bản Form, đến SuspendResumecho đồng hồ trò chơi.

Đây là riêng tư, vì vậy bạn sẽ cần sử dụng sự phản chiếu để mở khóa các sự kiện (ở đâu thislà a Game):

this.host.Suspend = null;
this.host.Resume = null;

Đây là câu thần chú cần thiết:

object host = typeof(Game).GetField("host", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
host.GetType().BaseType.GetField("Suspend", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(host, null);
host.GetType().BaseType.GetField("Resume", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(host, null);

(Bạn có thể ngắt kết nối chuỗi ở nơi khác

Sau đó, bạn cần cung cấp nguồn đánh dấu của riêng mình, khi XNA không đánh dấu Application.Idle. Tôi chỉ đơn giản là sử dụng một System.Windows.Forms.Timer:

timer = new Timer();
timer.Interval = (int)(this.TargetElapsedTime.TotalMilliseconds);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();

Bây giờ, timer_Tickcuối cùng sẽ gọi Game.Tick. Bây giờ đồng hồ không bị treo, chúng tôi chỉ cần tiếp tục sử dụng mã thời gian riêng của XNA. Dễ dàng! Không hoàn toàn: chúng tôi chỉ muốn đánh dấu thủ công của chúng tôi xảy ra khi đánh dấu tích hợp sẵn của XNA không xảy ra. Đây là một số mã đơn giản để làm điều đó:

bool manualTick;
int manualTickCount = 0;
void timer_Tick(object sender, EventArgs e)
{
    if(manualTickCount > 2)
    {
        manualTick = true;
        this.Tick();
        manualTick = false;
    }

    manualTickCount++;
}

Và sau đó, Updateđặt mã này để đặt lại bộ đếm nếu XNA đang đánh dấu bình thường.

if(!manualTick)
    manualTickCount = 0;

Lưu ý rằng đánh dấu này là chính xác ít hơn đáng kể của XNA. Tuy nhiên, nó vẫn nên ít nhiều xếp hàng với thời gian thực.


Vẫn còn một lỗ hổng nhỏ trong mã này. Win32, ban phước cho khuôn mặt xấu xí ngu ngốc của nó, về cơ bản dừng tất cả các tin nhắn trong vài trăm mili giây khi bạn nhấp vào thanh tiêu đề của cửa sổ, trước khi chuột thực sự di chuyển (xem lại liên kết đó ). Điều này ngăn không cho chúng tôi Timer(dựa trên thông điệp) đánh dấu.

Vì hiện tại chúng tôi không tạm dừng đồng hồ trò chơi của XNA, khi bộ đếm thời gian của chúng tôi bắt đầu tích tắc trở lại, bạn sẽ nhận được một loạt các bản cập nhật. Và thật đáng buồn, XNA giới hạn thời gian tích lũy đến mức thấp hơn một chút so với thời gian Windows dừng tin nhắn khi bạn nhấp vào thanh tiêu đề. Vì vậy, nếu người dùng tình cờ nhấp và giữ thanh tiêu đề của bạn, mà không di chuyển nó, bạn sẽ nhận được một loạt các bản cập nhật giảm một vài khung hình trong thời gian thực.

(Nhưng điều này vẫn đủ tốt cho hầu hết các trường hợp. Bộ đếm thời gian của XNA đã hy sinh độ chính xác để tránh nói lắp, vì vậy nếu bạn cần thời gian hoàn hảo , bạn nên làm một việc khác.)


2
Câu trả lời toàn diện tốt đẹp.
MichaelHouse

1
Tại sao chính xác bạn đã đặt câu hỏi, sau đó trả lời ngay lập tức?
Alastair Forge

4
Câu hỏi công bằng. Nó được khuyến khích rõ ràng . UI câu hỏi thậm chí có hộp "Trả lời câu hỏi của riêng bạn". Ban đầu tôi sẽ làm cho nó trở thành một câu trả lời cho câu hỏi này , nhưng đó chỉ là một bản chỉnh sửa của một câu trả lời cũ, và giải pháp của tôi không giải quyết được một phần của câu hỏi đó. Bằng cách này, nó có được khả năng hiển thị tốt hơn (là mới và là XNA trên GDSE thay vì SO). Và thông tin phù hợp hơn ở đây, hơn là blog của tôi.
Andrew Russell
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.