2012-06-13 24 views
24

Tôi hiện đang làm việc trên một trò chơi và tôi muốn có một menu chính với hình nền.Graphics.DrawImage có quá chậm để có hình ảnh lớn hơn không?

Tuy nhiên, tôi thấy phương thức Graphics.DrawImage() thực sự chậm. Tôi đã thực hiện một số đo lường. Giả sử rằng MenuBackground là hình ảnh tài nguyên của tôi với độ phân giải 800 x 1200 pixel. Tôi sẽ vẽ nó lên một bitmap 800 x 1200 khác (trước tiên tôi render mọi thứ vào một bitmap đệm, sau đó tôi mở rộng nó và cuối cùng vẽ nó lên màn hình - đó là cách tôi xử lý khả năng của các độ phân giải của nhiều người chơi. theo bất kỳ cách nào, xem đoạn tiếp theo).

Vì vậy, tôi đã đo đoạn mã sau:

Stopwatch SW = new Stopwatch(); 
SW.Start(); 

// First let's render background image into original-sized bitmap: 

OriginalRenderGraphics.DrawImage(Properties.Resources.MenuBackground, 
    new Rectangle(0, 0, Globals.OriginalScreenWidth, Globals.OriginalScreenHeight)); 

SW.Stop(); 
System.Windows.Forms.MessageBox.Show(SW.ElapsedMilliseconds + " milliseconds"); 

Kết quả là yên tĩnh đáng ngạc nhiên đối với tôi - các biện pháp Stopwatch một cái gì đó giữa 40 - 50 milliseconds. Và bởi vì hình nền không phải là thứ duy nhất được vẽ, toàn bộ menu mất khoảng hơn 100 ms để hiển thị, điều này hàm ý sự chậm trễ quan sát được.

Tôi đã cố gắng vẽ nó vào đối tượng Đồ họa do sự kiện Paint đưa ra, nhưng kết quả là 30 - 40 milliseconds - không có nhiều thay đổi.

Vì vậy, có nghĩa là, Graphics.DrawImage() không thể sử dụng để vẽ hình ảnh lớn hơn? Nếu vậy, tôi nên làm gì để cải thiện hiệu suất của trò chơi?

+1

Nếu bằng "không sử dụng được", bạn có nghĩa là "chậm như một con ốc trong mật đường", sau đó có. Như những người khác đã nói, hãy thử XNA. Dính vào rendering 2D sprite cho đến khi bạn quen với framework XNA, sau đó chuyển sang 3D nếu bạn nghiêng. – CodeHxr

+1

Tôi upvoting rằng chỉ cho tương tự. :) – Ani

Trả lời

84

Có, quá chậm.

Tôi đã gặp phải vấn đề này vài năm trước trong khi phát triển Paint.NET (ngay từ đầu, thực sự, và nó khá là bực bội!). Hiệu suất hiển thị là không đáng kể, vì nó luôn luôn tỷ lệ thuận với kích thước của bitmap chứ không phải kích thước của vùng mà nó được yêu cầu vẽ lại. Tức là, tốc độ khung hình giảm xuống khi kích thước của bitmap tăng lên, và tốc độ khung hình không bao giờ tăng lên khi kích thước của vùng không hợp lệ/vẽ lại giảm xuống khi triển khai OnPaint() và gọi Graphics.DrawImage(). Một bitmap nhỏ, nói 800x600, luôn hoạt động tốt, nhưng hình ảnh lớn hơn (ví dụ: 2400x1800) rất chậm. (Tuy nhiên, bạn có thể giả định, cho đoạn trên, rằng không có gì thêm xảy ra, chẳng hạn như mở rộng quy mô với một số bộ lọc Bicubic đắt tiền, có thể ảnh hưởng xấu đến hiệu suất.)

Có thể bắt WinForms sử dụng GDI thay vì GDI + và tránh ngay cả việc tạo ra một đối tượng Graphics đằng sau hậu trường, tại thời điểm này bạn có thể lớp một bộ công cụ kết xuất khác lên trên đó (ví dụ: Direct2D). Tuy nhiên, nó không đơn giản. Tôi làm điều này trong Paint.NET, và bạn có thể thấy những gì cần thiết bằng cách sử dụng một cái gì đó như Reflector trên lớp có tên là GdiPaintControl trong DLL SystemLayer, nhưng đối với những gì bạn đang làm, tôi sẽ xem xét nó là phương sách cuối cùng.Tuy nhiên, kích thước bitmap bạn đang sử dụng (800x1200) vẫn hoạt động đủ OK trong GDI + mà không cần phải dùng đến interop nâng cao, trừ khi bạn đang nhắm mục tiêu thứ gì đó thấp đến mức 300MHz Pentium II. Dưới đây là một số mẹo có thể giúp đỡ:

  • Nếu bạn đang sử dụng một bitmap đục (không có alpha/minh bạch) trong cuộc gọi đến Graphics.DrawImage(), và đặc biệt nếu đó là một bitmap 32-bit với một kênh alpha (nhưng bạn biết không rõ ràng hoặc bạn không quan tâm), sau đó đặt Graphics.CompositingMode thành CompositingMode.SourceCopy trước khi gọi DrawImage() (hãy nhớ đặt giá trị này trở về giá trị ban đầu sau, nếu không thường xuyên vẽ nguyên thủy sẽ rất xấu). Điều này bỏ qua rất nhiều pha trộn thêm tính toán cho mỗi pixel.
  • Đảm bảo Graphics.InterpolationMode không được đặt thành một cái gì đó như InterpolationMode.HighQualityBicubic. Sử dụng NearestNeighbor sẽ là nhanh nhất, mặc dù nếu có bất kỳ kéo dài nó có thể không nhìn rất tốt (trừ khi nó kéo dài chính xác 2x, 3x, 4x, vv) Bilinear thường là một sự thỏa hiệp tốt. Bạn không nên sử dụng bất kỳ thứ gì trừ NearestNeighbor nếu kích thước bitmap khớp với khu vực bạn đang vẽ, tính bằng pixel.
  • Luôn vẽ vào đối tượng Graphics được trao cho bạn trong OnPaint().
  • Luôn vẽ bản vẽ của bạn ở OnPaint. Nếu bạn cần vẽ lại khu vực, hãy gọi Invalidate(). Nếu bạn cần bản vẽ hiện tại, hãy gọi Update() sau Invalidate(). Đây là một cách tiếp cận hợp lý vì các tin nhắn WM_PAINT (kết quả trong một cuộc gọi đến OnPaint()) là các thông báo "ưu tiên thấp". Bất kỳ xử lý nào khác của trình quản lý cửa sổ sẽ được thực hiện trước tiên, và do đó bạn có thể kết thúc với rất nhiều khung bỏ qua và cản trở khác.
  • Sử dụng System.Windows.Forms.Timer làm tốc độ khung hình/thời gian đánh dấu sẽ không hoạt động tốt. Chúng được thực hiện bằng cách sử dụng số SetTimer của Win32 và kết quả là các tin nhắn WM_TIMER mà sau đó dẫn đến sự kiện Timer.Tick được nâng lên và WM_TIMER là một thông điệp ưu tiên thấp khác chỉ được gửi khi hàng đợi tin nhắn trống. Bạn nên sử dụng System.Threading.Timer và sau đó sử dụng Control.Invoke() (để đảm bảo bạn đang ở đúng chủ đề!) Và gọi số Control.Update().
  • Nói chung, không sử dụng Control.CreateGraphics(). (Hệ quả là 'luôn luôn vẽ trong OnPaint()' và 'luôn sử dụng Graphics cho bạn bởi OnPaint()')
  • Tôi khuyên bạn không nên sử dụng trình xử lý sự kiện Paint. Thay vào đó, hãy triển khai OnPaint() trong lớp bạn đang viết nên xuất phát từ Control. Bắt nguồn từ một lớp khác, ví dụ: PictureBox hoặc UserControl, sẽ không thêm bất kỳ giá trị nào cho bạn hoặc sẽ thêm phí bổ sung. (BTW PictureBox thường bị hiểu lầm. Bạn có thể hầu như không bao giờ muốn sử dụng nó.)

Hy vọng điều đó sẽ hữu ích.

+2

bạn xứng đáng tấn upvotes.For vẽ một lưới nền là có bất kỳ tip? TextureBrush có vẻ nhanh nhưng nó không hoạt động khi bạn có tùy chỉnh scalling với một yếu tố zoom vì nó không phù hợp với – GorillaApe

+1

Tối ưu hóa tốt nhất của bài đăng này cho tôi là chế độ nội suy. Thay đổi 'Bilinear' thành' RecentNeighbor' làm cho bản vẽ nhanh gấp 8 lần. – Pietu1998

+0

Nhiều năm sau, bạn vẫn đang giúp mọi người với bức tranh trong winforms – Nathan

3

GDI + có lẽ không phải là lựa chọn tốt nhất cho trò chơi. DirectX/XNA hoặc OpenGL nên được ưa thích vì chúng sử dụng bất kỳ khả năng tăng tốc đồ họa nào là có thể và rất nhanh.

3

GDI + không phải là ma quỷ tốc độ theo bất kỳ cách nào. Bất kỳ thao tác hình ảnh nghiêm trọng nào thường phải đi vào bên bản địa của mọi thứ (pInvoke các cuộc gọi và/hoặc thao tác thông qua một con trỏ thu được bằng cách gọi LockBits.)

Bạn đã xem xét XNA/DirectX/OpenGL chưa ?. Đây là những khuôn khổ được thiết kế để phát triển trò chơi và sẽ có các đơn đặt hàng có cường độ hiệu quả và linh hoạt hơn so với sử dụng một khung giao diện người dùng như WinForms hoặc WPF. Các thư viện đều cung cấp các ràng buộc C#.

Bạn có thể đưa vào mã gốc, sử dụng các chức năng như BitBlt, nhưng cũng có chi phí liên quan đến việc vượt qua ranh giới mã được quản lý.

+0

Tôi cũng khuyên bạn nên sử dụng OpenGL/DirectX. XNA không thực sự là lựa chọn TỐT NHẤT cho mỗi người vì nhiều lý do. Dễ sử dụng, có - nhưng về lâu dài nó là một chút của một khuyết tật. – Ani

+0

@ananthonline: Bạn nói đúng, chúng đáng được đề cập đến. DirectX vượt trội so với OpenGL nếu bạn không quan tâm đến hỗ trợ đa nền tảng. Bạn có thể mở rộng nhận xét của mình không * "về lâu dài [XNA là] một chút điểm chấp" *.Tôi có thể có một số lỗ hổng trong kiến ​​thức của mình vì tôi không phải là nhà phát triển trò chơi nghiêm túc, chỉ là sở thích của tôi. –

+0

XNA đưa ra một số giả định cho việc sử dụng nó. Một trong những hạn chế nghiêm trọng mà tôi có thể nghĩ là nhiều mục tiêu không thể chia sẻ một bộ đệm độ sâu, làm cho các tình huống phổ biến như hiệu quả làm việc hiển thị trả chậm không thể. Xem tại đây để biết thêm thông tin (http://forums.create.msdn.com/forums/p/64179/397431.aspx) – Ani

Các vấn đề liên quan