12/08/2018, 11:39

Tối ưu hoá game trong unity

Định nghĩa tối ưu hoá Tối ưu hoá là một phần trong quá trình phát triển game ,nói một cách đơn giản nó là quy trình để cải tiến tính hiệu quả của hiệu năng game của bạn. Vấn đề tối ưu hoá này đặc biệt quan trọng trong quá trình phát triển game trên mobile . Mỗi nền tảng phát triển game đều ...

  1. Định nghĩa tối ưu hoá

Tối ưu hoá là một phần trong quá trình phát triển game ,nói một cách đơn giản nó là quy trình để cải tiến tính hiệu quả của hiệu năng game của bạn. Vấn đề tối ưu hoá này đặc biệt quan trọng trong quá trình phát triển game trên mobile .

Mỗi nền tảng phát triển game đều có những hạn chế về năng lực xử lý của CPU(bộ xử lý trung tâm) và GPU (bộ xử lý đồ hoạ) .Đặc biệt trong các cảnh game 3D với độ chi tiết cao thì nhu cầu xử lý sẽ càng cao . Khi nhu cầu xử lý này trở nên quá tải thì hiệu năng game sẽ giảm và gây cảm giác khó chịu cho người dùng như là load game lâu, game chạy chậm và bị giật hoặc nghiêm trọng hơn thì có thể bị treo hệ thống .Quy trình tối ưu hoá là chuỗi các quyết định hay thoả hiệp giữa việc duy trì hiệu năng game và chất lượng đồ hoạ .

2.** Hiệu năng hướng tới**

hiệu năng hướng tới thường được biểu thị theo tốc độ frame mong muốn hay số frame trên giây (fps). Nếu tốc độ frame thấp thì người chơi mất cảm giác chuyển động .Tốc độ frame cao sẽ mang lại trải nghiệm trực quan tốt hơn nhưng trong các game phức tạp đòi hỏi nhiều xử lý trên frame vẫn có thể chạy trơn chu trong một khoảng tốc độ frame vừa phải nhưng vẫn chấp nhận được .

Việc xác định hiệu năng hướng tới đầu tiên bạn nên biết rõ game của mình chạy trên nền tảng nào .Nếu game chạy trên các máy console và PC có bộ xử lý mạnh mẽ thì có thể có thể ta không cần quan tâm lắm nhưng trên nền tảng di động thì chúng ta bắt buộc phải quyết định sớm trong quá trình thiết kế game để hiệu năng game tối ưu nhất có thể. Đặc điểm hạn chế của các thiết bị di động là cấu hình thấp cả về CPU và GPU , hơn nữa trên thị trường có rất nhiều loại thiết bị khác nhau với cấu hình cũng không hề giống nhau vì vậy việc để game của ta có thể chạy tốt ở tất cả các máy là một vấn đề lớn .Chúng ta phải điều tra các thiết bị phổ biến để xác định một tốc độ frame hợp lý để game chạy tốt trên nhiều máy nhất có thể .

3.** Theo dõi mục tiêu**

Trong trình soạn thảo Unity bạn hãy chạy một cảnh bất kỳ. Trong menu trên cùng của khung nhìn Game bạn hãy nhấn nút Stats , bạn sẽ thấy một bảng các thông số thống kê hiệu năng hiện ra gồm cả tốc độ frame trên giây (FPS) .

3.png

chỉ số Batches thậm chí còn chỉ ra thông tin chi tiết hơn đó là số lượng vật mà CPU cần render .Việc hiểu rằng một đối tượng game có thể được render nhiều lần trên frame đối với mỗi hiệu ứng áp dụng trên đối tượng này là rất quan trọng, chẳng hạn như ánh sáng ,đổ bóng và phản chiếu điểm ảnh .Ta nên dùng ít các hiệu ứng như vậy hơn bằng cách chỉ áp dụng khi chúng có đóng góp đáng kể cho giao diện game sẽ giảm bớt các lời gọi hàm vẽ draw call , nghĩa là giảm bớt công việc cho CPU.

Ta có thể thiết lập tốc độ frame nhắm đến bằng cách sử dụng một script đơn giản .

public int frameRate = 50;
void Awake()
{
	Application.targetFrameRate = frameRate;
}

sau đó áp dụng script này vào scene chúng ta cần thiết lập tốc độ frame. Chú ý rằng tốc độ frame sẽ không chính xác bằng 50 thậm chí có thể không có tác dụng gì cả đó là bởi vì tốc độ frame nhắm tới không phải là một giới hạn tuyệt đối .Nếu muốn xem sự khác nhau đáng chú ý ta hãy chỉnh frameRate = 25; sau đó chạy lại bạn sẽ thấy tốc độ frame bây giờ sẽ trong khoảng gần 25 hơn. Khi bạn chỉnh về -1 thì đây là giá trị mặc định , giá trị này thiết lập game về tốc độ 50-60 fps và báo cho các thiết bị render nhanh nhất có thể .

Thời điểm nào và nơi nào cần tối ưu hoá

Các quyết định tối ưu hoá được thực hiện trong giai đoạn thiết kế phần lớn dựa trên những giới hạn đã biết về nền tảng nhắm đến .Trong quá trình phát triển , việc tuân theo các khuyến nghị về kinh nghiệm thực tiễn tốt nhất sẽ tránh việc hiệu năng bị suy giảm bởi những kỹ thuật phát triển kém hiệu quả .Ngoài ra quá trình tối ưu hoá khá động liên quan tới những đặc trưng riêng của game khi các chi tiết nhiều vô số , điều này có thể tác động tới hiệu năng game.

Giai đoạn thiết kế

giai đoạn thiết kế sẽ có nhiều quyết định hơn là chỉ chọn tốc độ frame nhắm đến.Khi xem xét đồ hoạ game khả năng của nên tảng nhắm đến sẽ được xem xét .Kinh nghiệm chung là không sử dụng thêm bất kỳ đỉnh nào cho một mô hình nhiều hơn số lượng cần thiết , tuy vậy giới hạn render của CPU phụ thuộc vào mọi thứ trong cảnh , gồm cả các yếu tố khác như việc sử dụng màu sắc , nguồn sáng và đổ bóng chứ không chỉ một mô hình hay một yếu tố nào khác .Bạn nên tham khảo các tài liệu hướng dẫn của nền tảng nhắm đến đối với các trường hợp cụ thể .

Các thiết bị di động không thể xử lý nhiều hơn 100 000 đỉnh còn PC thì có thể xử lý nhiều hơn tới vài triệu đỉnh . Với game di động bạn có thể chọn một mức chi tiết nền thấp hơn nhiều mứa so với khi bạn dùng trên game PC để bảo toàn các chi tiết của mô hình đối tượng game chính .

Một giới hạn khác nữa của thiết bị di động là kích thước file tải về . Việc kết hợp tái sử dụng các mô hình , texture và chất liệu vào thiết kế game sẽ giảm kích thước file chung .

Giai đoạn phát triển

Ta nên xem xét câu châm ngôn tôn vinh thời gian "Nếu thứ gì chưa hỏng thì đừng sửa" nhớ rằng mục đích tối ưu hoá là để duy trì mức độ hiệu năng game mà bạn nhắm đến . Khi game trình diễn với mức độ hiệu năng đó từ lúc bắt đầu cho tới lúc hoàn tất thì công việc của bạn hoàn tất .

Tác động trực quan trên người chơi đóng góp đáng kể đến trải nghiệm game, đó là lý do vì sao các công cụ đồ hoạ game đi chi tiết tới mức điểm ảnh và yêu cầu nhiều lần render để kết hợp mọi chi tiết cuối cùng của nguồn sáng ,phản chiếu ,đổ bóng ... Đồ hoạ trực quan không chỉ là tác nhân duy nhất có thể cản trở hiệu năng game .Vật lý , animation,âm thanh, và nhiều thứ khác cũng có thể khiến cho hiệu năng game bị ảnh hưởng.

Khi bạn xây dựng game và test lại nhiều lần , sự tắc nghẽn có thể xuất hiện ở một số điều kiện đáng chú ý của gameplay , chẳng hạn khi một mô hình phức tạp xuất hiện trong khung hình ,trong một chuỗi animation cụ thể,hay kèm theo hệ thống hiệu ứng hạt . Tốt nhất chúng ta nên trực tiếp giải quyết các vấn đề đó .

Nếu nguyên nhân tắc nghẽ không rõ ràng , Unity có nhiều công cụ giúp bạn xác định . Editor Profiler là một công cụ chuyên dụng để đánh giá game cả trên Editor hay trên thiết bị kiểm thử , tiếc là công cụ này chỉ có trên bản Unity Pro . Các công cụ profiler dựng sẵn đã có dành cho cả iOS và android . Tuy vậy bạn vẫn có thể có được các thông tin hữu ích về game của mình từ Build log .

**Cách tạo build log **

Trong menu trên cùng trình soạn thảo Unity chọn File-> build setting . Hãy chắc chắn chọn tuỳ chọn PC, Mac & Linux Stangalone trong cửa sổ plasform để Unity build được kiểu file phù hợp cho nền tảng bạn nhắm đến . Sau đó nhấn build rồi chọn nơi lưu file rồi nhấn save .

2.png

Sau khi build hoàn tất bạn có thể truy cập build log bằng cách chọn tab Console trong bảng Project , tìm biểu tượng menu thả xuống ở góc trên bên phải rồi chọn Open Editor Log .

4.png

Build log sẽ hiện ra trong cửa sổ Console .Cùng với các thông tin khác ,bạn có thể thấy danh sách các kiểu tài nguyên tạo nên game cùng với kích thước tương đối của chúng

1.png

Ở ngay bên dưới bạn sẽ thấy danh sách Used Assets (tài nguyên đã dùng) . Thời gian tải game bị ảnh hưởng bởi kích thước này , còn người chơi game thì muốn được chơi game ngay .Vậy ta nên tối ưu hoá ở đây bằng cách bỏ đi các tài nguyên không dùng đến trong dự án khi phát triển . Người tải game chú ý tới kích thước file tải về và họ không hề muốn tải về một file nặng , hơn nữa nhà cung cấp dịch vụ cũng giới hạn kích thước file tải về .Vì vậy ta nên giảm sao cho kích thước file tải về càng nhỏ càng tốt . Bằng cách so sánh danh sách Used Assets với nội dung thư mục Assets bạn có thể xoá đi một cách an toàn các file không dùng đến .

**Tối ưu hoá khi viết code trong các Script **

  • _Đặt biến kiểu tĩnh và chỉ thị #pragma strict _

Trình soạn thảo MonoDevelop tạo ra script cho bạn và luôn bắt đầu với dòng #pragma strict . dòng này báo cho trình biên dịch biết để dịch đoạn mã theo sau "một cách chặt trẽ" (strictly) ,tức là bắt buộc bạn dùng kiểu tĩnh nếu không sẽ có lỗi biên dịch được ném ra .

Đặt kiểu tĩnh ám chỉ việc đặt kiểu biến khi bạn khai báo chúng .Trong dòng mã sau bạn khai báo tường minh biến myValue thuộc kiểu int

var myValue : int = 2;

ngoài ra bạn có thể khai báo như sau

var myValue = 2;

Unity sẽ tự động chuyển biến này thành mã kiểu tĩnh sử dụng phương pháp có tên là suy diễn kiểu (type inference) . Mặc dù khả năng này cho phép viết mã đơn giản hơn nhưng nếu biến không thể suy diễn kiểu được thì Unity sẽ phụ thuộc vào việc định kiểu động .

Trong định kiểu động (dynamic typing) , Unity phải chỉ ra kiểu biến nào dựa trên giá trị được gán cho biến . Việc "chỉ ra" này sẽ làm tốn thời gian và do đó ảnh hưởng đến hiệu năng game . Việc dùng định kiểu tĩnh sẽ giúp cho hiệu năng game ổn định , còn chỉ thị #pragma strict là để đảm bảo bạn làm điều này .

Lưu đệm các tìm kiếm component

Kỹ thuật viết script này thường hiệu quả với các script được dùng thường xuyên ,nhưng đổi lại đòi hỏi viết nhiều mã hơn một chút . Hàm GetComponent() là một ví dụ về tìm kiếm (lookup) . Việc tìm component trong đối tượng game sẽ tiêu tốn thời gian và ảnh hưởng tới hiệu năng . Ý tưởng ở đây là tìm tham chiếu một lần duy nhất rồi lưu đệm hoặc lưu tham chiếu trong một biến private để sẵn sàng sử dụng trong các script về sau . Nói cách khác hãy tránh dùng hàm GetComponent() trong hàm Update() hoặc hàm FixedUpdate() bất cứ nơi nào có thể .

**Các phương pháp tối ưu Script nâng cao **

Bất kỳ ở chỗ nào bạn có thể giảm và tránh các tính toán theo từng frame đều sẽ giups cải thiện hiệu năngg . Một trong số đó là :

  • Sử dụng trigger

ví dụ để xoá một viên đạn đi bằng cách theo dõi hàm Destroy() sau 2 giây bạn viết mã như sau :

Destroy(bullet, 2);

Mặc dù đây là sự cải tiến để tránh việc có quá nhiều viên đạn trong chương trình , nhưng Unity vẫn phải theo dõi mỗi đối tượng viên đạn và kiểm tra vòng đời của chúng cho tới khi nào hết 2 giây . Một giải pháp khác là hàm Destroy() sẽ sử dụng khi viên đạn chạm vào một vùng trigger nào đó mà ta chỉ định thay vì việc phải kiểm tra liên tục .

void OnTriggerEnter(Collider other)
{
	Destroy(bullet);
}

Object pooling

Object pooling lấy việc tối ưu hoá mô hình làm sẵn . Việc tạo thể hiện và huỷ đối tượng cũng chiếm một chi phí tính toán đáng kể . Thay vì tạo thể hiện và huỷ hết đối tượng này đến đối tượng khác thì một mảng các mô hình đối tượng game làm sẵn sẽ được tạo ra dưới dạng "pool" (bể chứa) để có thẻ lấy ra đối tượng khi cần rồi trả lại đối tượng khi không sử dụng nữa .

Coroutine

Corooutine có thể tạm ngừng việc thực thi của nó trong một frame rồi lấy lại và tiếp tục thực thi trong frame tiếp theo qua bất kỳ độ dài frame nào . Khi bạn gọi một hàm khác từ trong hàm Update() hay FixedUpdate() thì hàm bạn gọi được chạy từ lúc bắt đầu cho tới lúc kết thúc một frame . Ở tốc độ là 60fps thì điều này nghĩa là hàm chạy 60 lần trên giây . Thường thì bạn không cần một hàm phải được gọi trong mọi frame , kể cả khi bạn cần chạy hàm đó định kỳ .Vậy từ nay những hàm mà bạn không cần gọi ở trong tất cả các frame thì bạn hãy sử dụng Coroutine . Thử so sánh một hàm chạy 60 lần trên giây và 10 lần trên giây mà hiệu năng game vẫn vậy thì bạn chọn cách nào , rõ ràng trong trường hợp này dùng Coroutine sẽ tăng hiệu năng lên 6 lần .

Tối ưu hoá với Mecanim

Dưới đây là một số lưu ý để dùng Mecanim hiệu quả hơn .

  • sử dụng bảng băm thay vì chuỗi để truy vấn Animator.

  • Triển khai AI Layer để điều khiển Animator . Bạn có thể dùng AI Layer để cung cấp các lời gọi callback đơn giản cho hàm OnStateChange(), OnTransitionBegin , ...

  • Sử dụng tag state để dễ dàng ánh xạ máy trạng thái AI với máy trạng thái Mecanim.

  • Sử dụng đường cong phụ trợ để mô phỏng Events .

  • Sử dụng đường cong phụ trợ để đánh dấu animation(ví dụ như kết hợp với việc ánh xạ mục tiêu)

  • Tối ưu hoá thời gian chạy :

    • Luôn tối ưu hoá animation bằng cách thiết lập mục Culling Mode của animator về thành Based on Renderers , và vô hiệu hoá thuộc tính Update when offscreen của trình render lưới da (skinned mesh renderer) . . Theo đó thì cách animation sẽ không phải cập nhật khi nhân vật chưa hiện ra .
    • Một kinh nghiệm nữa là nên dùng ID bảng băm (hash ID) Khi làm việc với animator , việc sử dụng định dạng bảng băm (hash identifier) là một phương pháp tối ưu hoá trong đó tên của các trạng thái và các tham số được gán là kiểu integer . Việc sử dụng kiểu integer thay vì sử dụng chuỗi sẽ giúp giảm chi phí xử lý phụ thêm .

    để tìm hiểu thêm bạn đi tới trang này : http://docs.unity3d.com/Manual/MecanimPeformanceandOptimization.html

0