XNA dừng cuộc gọi Update
và Draw
trong 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.)
XNA dừng cuộc gọi Update
và Draw
trong 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:
Đú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 Game
bên dưới Form
bị 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 Idle
bắn.
Vì vậy, nếu Game
khô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 Suspend
và Resume
cho đồ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 this
là 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_Tick
cuố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 và 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.)