Just 4 fun!

Of course I can change the world, If you give me a source code

Post Ads (Documentation)

Author Info (Documentation)

style="display:block"
data-ad-client="ca-pub-2623284640924516"
data-ad-slot="8347113188"
data-ad-format="auto">

Wednesday, December 28, 2016

Khi to và lâu không phải là lợi thế!

Code game là phải nhỏ và nhanh

       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ạ. Vậy chúng ta cần phải tối ưu những gì và cụ thể như thế nào? Well, hãy đọc hết bài này đi nhé! J

1. Hạn chế dùng Update
Hàm update được gọi mỗi lần cho 1 frame(khung hình). Và trong toàn bộ các object có hàm update thì nó gọi khá là nhiều, ngoài ra còn có FixUpdate và LateUpdate cũng được gọi thường xuyên. Chức năng này được gọi đến để cập nhật các hành vi và sự kiện theo thời gian, nên dùng trong trường hợp cần thiết, nếu lạm dụng update thì sẽ rất ảnh hưởng đến performance. 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 độ 60fps thì điều này có nghĩa là hàm đó sẽ được gọi 60 lần trên giây, thấy sự lãng phí ở đây chưa? Vậy điều đầu tiên cần nhớ đó là hạn chế sử dụng tính toán và gọi hàm từ Update nếu bạn không muốn game của mình chạy ỳ ạch và ngốn hết tài nguyên hệ thống. Để thay thế thì có nhiều cách, như ta có thể dùng Coroutine hoặc InvokeRepeat
-      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 . 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.

-      InvokeRepeat dùng để gọi hàm sau một số giây nhất định, gần giống với Corooutine, cụ thể khác nhau ở những điểm nào mình sẽ có một bài viết phân tích sau J

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

5.Sử dụng Static Batching
        Đối với các object tĩnh, ví dụ như bàn, background tĩnh,… không di chuyển trong suốt quá trình chơi game, thì ta có thể chọn vào Static:






Unity sẽ tối ưu hóa, giảm số draw calls và tăng hiệu suất làm việc hơn.
6. Tối ưu hoá với Mecanim
Dưới đây là một số lưu ý để dùng Mecanim hiệu quả hơn .
  • 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 .

7. 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ể. Ví dụ:
Thay vì gọi trực tiếp : gameObject.getComponent<Rigidbody2D>() liên tục mỗi khi cần dùng đến, ta dùng:

Rigidbody2D mRigidbody2D;
Void Start() {
      mRigidbody2D = gameObject.getComponent<Rigidbody2D>();
}

Bây giờ, nếu muốn sử dụng thì chỉ cần dùng mRigidbody2D.
Đặc biệt đối với transform ( và một số thuộc tính tương tự, như audio…) cũng nên dùng cách này để tránh tham chiếu trực tiếp component của objects.

Void Start(){
     mTransform = transform;
}
Void Update(){
     mTransform.localPosition = mPosition;
}

    Điều này cũng đúng khi dùng Hash ID khi làm việc với animator, thông thường bạn sẽ code theo kiểu như thế này:




Animator anim;
//...
anim.SetBool("isRun",true);
anim.SetFloat("speed",5f);
...

Hãy thử tưởng tượng khi làm một game lớn với số lượng animation cực khủng, thì việc truy cập thông qua các chuỗi string như thế này sẽ ảnh hưởng tới performance. Hoặc là khi muốn đổi tên 1 chuỗi string, bạn phải tìm và đổi rất nhiều nơi, và thường gặp lỗi chính tả khi viết các chuỗi string. Unity cung cấp cho chúng ta Hash id, chuyển các chuỗi string về kiểu int trong Animator.

public int isRun;
void Awake(){
   isRun = Animator.StringtoHash("isRun");
}
//.......
anim.SetBool(Hash.isRun,true);

Bạn nên để tất cả các hash id này vào 1 class để dễ dàng quản lý.
P/s: Đối với tag và layer cũng là các chuỗi string, trong quá trình code, bạn nên tập trung vào 1 class khai báo quản lý. Và cũng nên khai báo 1 class để chứa và quản lý các hằng số trong game, dễ quản lý và bảo trì.





1 comment: