Transform trong Unity là gì

kiến thức cơ bản về transform trong unity mà bạn cần biết

Giới thiệu

Transform là một chức năng cần thiết để phát triển chò trơi trong Unity. Nó chịu trách nhiệm cho các chức năng quan trọng xuất hiện nhiều lần trong trò chơi như di chuyển, xoay, scalenhóm. Transform luôn được thao tác khi di chuyển đối tượng trong quá trình phát triển trò chơi.

Component đặc biệt Transform

Đầu tiên cần nói đến chính là Transform là một component. Một component là một chức năng trong Unity. Transform là một component vì vậy bạn có thể coi nó như một chức năng tương tự như Camera hay Light.

Các chức năng của transform là Position, Rotation, ScaleGroup

Nó có 3 điểm đặc biệt sau:

  • Luôn được gán cho GameObject
  • Không thể bị xoá khỏi GameObject
  • Không cần GetComponent để có thể truy cập Transform

Transform luôn được gán cho GameObject

Không có GameObject nào mà không có Transform

Tất cả các GameObject đều có các chức năng cơ bản như di chuyển và xoay do Transform thực hiện

Không thể xóa Transform

Như đã nói ở trên Transform luôn tồn tại trong GameObject vì vậy không có cách nào để loại bỏ Transform khỏi GameObject

Như thấy trong hình dưới đây không hề có menu “Remove Component”

Còn đối với các component bình thường ta có thể thấy menu “Remove Component” để xóa component khỏi GameObject

Nếu bạn cố gắng xóa component Transform bằng code thông qua phương thức Destroy

Một lỗi như sau sẽ hiện thị ra màn hình console

Nói cách khác không thể destroy component Transform

Không thể thêm nhiều hơn 1 Transform

Không thể thêm Transform nhiều hơn 1 lần. hãy thêm bằng cách nhấn vào nút Add Component

Như bạn có thể thấy ở trên Transform không tồn tại trong danh sách hiển thị ra. Vì nó là component không bao giờ được thêm vào vì nó được gắn vào GameObject ngay từ đầu nên nó bị loại bỏ khỏi danh sách

Hãy thử thêm bằng cách sử dụng đoạn code sau

Một thông báo sẽ được hiển thị ra console khi đoạn code trên được thực thi nói cho bạn biết rằng không thể thêm component Transform vì nó đã tồn tại trước đó trong GameObject rồi

GetComponent là không bắt buộc để có thể sử dụng Transform

Để lấy component chúng ta thường sử dụng GetComponent nhưng Transform là component đặc biệt và nó không cần phải sử dụng GetComponent để có thể sử dụng được. Hãy xem đoạn code sau:

Notes

RectTransform là Transform nhưng sử dụng cho UI (user interface).

Nó chịu trách nhiệm về các chức năng như position, rotation, scale, group trong canvas

Bản thân RectTransform là kế thừa từ Transform nên nó có thể coi là Transform nhưng có thêm các chức năng bổ sung phục vụ cho UI

Các cách cơ bản để sử dụng Transfrom

  1. Thay đổi vị trí của Transform bằng position hoặc localPosition
  2. Thay đổi góc xoay của Transform bằng eulerAngles
  3. Thay đổi tỉ lệ của Transform bằng localScale
  4. Gom nhóm Transform bằng SetParent

Thay đổi vị trí của Transform bằng localPosition / position

Trong quá trình phát triển trò chơi, bạn có thể muốn thay đổi vị trí của nhân vật của mình. Thay đổi vị trí là nhiệm vụ của Transform. Bạn có thể thực hiện bằng cách thay đổi giá trị của thuộc tính position trong inspector

Bạn có thể thay đổi các tọa độ x, y, z

Sử dụng position hoặc localPosition trong code như thế nào? hãy xem ví dụ di chuyển tọa độ x của Transform từ -2 thành 2 sau 1 giây

Bạn có thể thấy rằng tọa độ X đã thay đổi sau 1 s

Sự khác biệt giữa position và localPosion là gì?

Chúng trông có vẻ giống nhau nhưng thật ra hoàn toàn khác nhau. Bạn cần hiểu chính xác điểm khác biệt giữa chúng để vận dụng trong thực tế

Đầu tiên bạn có thể hiểu là

  • position : là tọa độ tuyệt đối
  • localPosition : là tọa độ tương đối
Tọa độ tuyệt đối “position” là gì?

trong unity tọa độ biểu diễn dưới dạng xyz có trục x, trục y, trục z và gốc tọa độ. Gốc tọa độ là điểm có tọa độ xyz (0, 0, 0)

Tọa độ tuyệt đối là tọa độ dựa trên gốc tọa độ, trong hình dưới đây nhân vật của bạn được đặt ở vị trí cách gốc tọa độ 2 đơn vị trên trục x

Ví dụ nhân vật ở tọa độ (1, 0, 2)

Tọa độ tuyệt đối còn được gọi là “global position

Tọa độ tương đối “localPosition” là gì?

Tọa độ tương đối là tọa độ liên quan đến hệ thống phân cấp

Hệ thống phân cấp thể hiện như hình minh họa sau đây

Để giải thích localPosition chúng ta sẽ sử dụng mối quan hệ parent-child đơn giản sau đây làm ví dụ

Đầu tiên hãy đặt vị trí parent trong hierarchy ở tọa độ (0, 1, 0)

Bây giờ chúng ta hãy di chuyển vị trí trục x của child đến vị trí (2, 0, 0)

Vị trí tương đối là vị trí so với tọa độ của parent, như ví dụ trên thì

  • localPosition của nhân vật sẽ là (2, 0, 0)
  • position của nhân vật sẽ là (2, 0, 0) + (0, 1, 0) = (2, 1, 0)
Phân biệt position và localPosition

Như đã giải thích về tọa độ tương đối và tọa độ tuyệt đối, nhưng làm thế nào để sử dụng chúng đúng cách.

Với hầu hết các nhu cầu bình thường chỉ sử dụng localPosition là đủ. Nếu transform hierarchy rất sâu (transform lồng nhau rất nhiều cấp) và bạn muốn di chuyển child hierarchy sang tọa độ tuyệt đối (global position) hãy sử dụng position

Chú ý răng việc sử dụng positon và localPosition cùng nhau trong trường hợp không cần thiết sẽ gây nhầm lẫn và khó hiểu. Vì vậy nên sử dụng localPosition cho hầu hết các thao tác thay đổi vị trí.

Đơn vị trong Unity là mét

Như tiêu đề đơn vị sử dụng trong Unity là mét

Nếu bạn đặt tọa độ là (3, 0, 0) nhân vật sẽ có khoảng cách với gốc tọa độ là 3m trên trục X.

Sử dụng đồng nhất đơn vị là điều quan trọng. Các model tạo bằng các phần mềm 3D có thể thêm vào unity và đơn vị của phần mềm 3D không phải lúc nào cũng là mét. hãy kiểm tra và chắc chắn nó được chuyển đổi thành mét để mọi tính toán của bạn sau này được đồng nhất và chính xác.

Vector3 là gì?

Vector3 là dữ liệu chứa 3 số bạn có thể truy cập nó với 3 thuộc tính x, y, z. Bạn có thể sử dụng nó để biểu diễn véc tơ trong không gian 3 chiều, điểm, tọa độ

Vector3 cũng có sẵn các chức năng hữu ích như các phép toán số học, hay tính khoảng cách giữa hai điểm. Lưu ý rằng Vector3 chứa các số thập phân, nếu bạn chỉ muốn xử lý số nguyên bạn có thể sử dụng Vector3Int

Di chuyển vị trí bằng phương thức Translate

Phương thức Translate cũng có thể thay đổi vị trí của Transform, tham số truyền vào là khoảng cách cần di chuyển đến.

Đoạn code trên sẽ cộng thêm (1f, 0.5f, 0f) vào tọa độ tương đối.

Nếu bạn muốn Translate bằng tọa độ tuyệt đối (global position) bạn có thể chỉ định đối số thứ 2 của phương thức Translate là Space.World

Thay đổi góc xoay bằng eulerAngles

Bạn có thể nhìn thấy thuộc tính Rotation trong cửa sổ Inspector

Bạn có thể xoay theo trục x, y, hoặc z

Thay đổi góc với localEulerAngles / eulerAngles

Cũng tương tự như localPosition / position các phép quay cũng có phép quay dựa trên gốc và phép quay dựa theo parent

Chạy thử đoạn mã trên bạn sẽ thấy nhân vật xoay một góc 45 độ theo trục z

localEulerAngles và eulerAngles xoay transform bằng cách thay đổi các giá trị vector3

Thay đổi góc với localRotation / rotation

Ngoài eulerAngle bạn có thể sử dụng rotation để thay đổi góc, rotation sẽ sử dụng Quaternion thay vì Vector3.

Quaternion được sử dụng để biểu diễn các phép quay.

Theo dõi đoạn code sau:

Để hiểu được quaternion, bạn cần có một số kiến thức toán học nhất định chúng ta sẽ tìm hiểu về nó chi tiết trong một bài viết cụ thể hơn.

Thay đổi tỉ lệ bằng localScale

Bạn có thể nhìn thấy thuộc tính Scale trong cửa sổ Inspector

Bạn có thể thay đổi tỉ lệ theo trục x, y, hoặc z

“Enable constrained proportions”: sẽ khiến cho thay đổi x, y, z biến đổi theo cùng tỉ lệ

Ví dụ:

Khác với position và rotation scale chỉ có một loại localScale mà thôi

Sử dụng setParrent để thay đổi hệ thống phân cấp

Cùng với position, rotation và scale một tính năng quan trọng khác của Transform là gom nhóm

Bạn có thể nhóm các đối tượng bằng cách kéo thả GameObject trong inspector

Ngoài ra bạn có thể sử dụng phương thức SetParent

Giờ thì gameObject sẽ là con của “_parent” sau khi hàm Start được thực thi.

Hãy chú ý đến tham số thứ 2 của phương thức SetParent nó rất quan trọng, giá trị mặc định là true nếu bạn không chỉ định.

  • true : positon, rotation, scale được thay đổi để đối tượng giữ nguyên vị trí ở global position như trước
  • false :

Để làm rõ hành vi của nó chúng ta xem xét ví dụ sau đây

ban đầu cả 3 đối tượng không ràng buộc với nhau không phải là cha con

thay đổi gameObject C thành con của A với tham số thứ 2 là true

Như bạn thấy vị trí của C được thay đổi thành (3.5, 1.5, 0) để global position của nó được giữ nguyên như ban đầu

thay đổi gameObject C thành con của A với tham số thứ 2 là false

localPosition của C lúc này bằng với position lúc trước của nó, lúc này C sẽ kế thừa tọa độ (6.5, -1.5, 0) của A

nói cách khác C được đặt tại vị trí (10, 0, 0) so với (6.5, -1.5, 0)

Đối số thứ 2 của SetParent thường được chỉ định là false, true hiếm khi được sử dụng

Nếu bạn truyền null vào SetParent. Điều này đồng nghĩa với việc loại bỏ parent của gameObject lúc này nó sẽ trở thành một đối tượng ngoài cùng trong hierarchy

1
 child.transform.SetParent(null);

Bạn có thể tham khảo thêm tài liệu của Unity về SetParent ở đây

Truy cập đối tượng parent

Bạn có thể truy cập đối tượng cha gần nhất của transform dựa vào thuộc tính parent

Giả sử ta có hệ thống hierarchy như sau

Đoạn code sau đây được gắn vào gameObject Child

Kết quả hiện thị ra màn hình console sẽ là “ParentB”

Parent trả về null

Nếu đối tượng của bạn đang ở gốc của hierarchy thuộc tính parent sẽ trả về kết quả null

vì vậy bạn nên kiểm tra kết quả với null trước khi sử dụng

Truy cập root

Đôi khi bạn muốn truy cập đối tượng root từ rất sâu trong hệ thống hierarchy. Trong trường hợp đó bạn có thể sử dụng thuộc tính root

Kết quả hiện thị ra màn hình console sẽ là “Root”.

Phụ lục

Phương thức LookAt

Trong quá trình phát triển trò chơi, bạn cần làm cho đối tượng của mình đối mặt với một hướng cụ thể. Ví dụ, trường hợp kẻ thù bắn đạn vào bạn. Kẻ thù sẽ đối mặt với bạn và bắn. Thoạt nghe có vẻ khó nhưng thực hiện rất dễ

Bạn cũng có thể truyền vector3 làm đối số cho phương thức LookAt

1
 transform.LookAt(new Vector3(5, 0, -5));

Transform animation

Animation của Transform thường được sử dụng trong suốt quá trình phát triển trò chơi của bạn. Dưới đây chúng ta cùng tìm hiểu một số cách thể thực hiện transform animation.

Sử dụng Update

Hãy xem xét đoạn code mô tả chuyển động đều (vận tốc không đổi) sau. Nhân vật sẽ di chuyển từ trái sang phải sau đó di chuyển ngược lại

Kết quả là bạn có được một chuyển động như dưới đây:

Sử dụng AnimationClip

Bạn cũng có thể tạo animation một cách trực quan bằng công cụ Animation có sẵn của Unity

Sử dụng Tween

Ngoài ra bạn cũng có thể sử dụng Tween (ví dụ ở đây sử dụng DOTween nó là một plugin của bên thứ 3). Giúp bạn tạo animation chỉ với một vài dòng code.

Và đây là kết quả

Bạn có thể thấy đoạn code sử dụng Dotween ngắn hơn và dễ hiểu hơn nhiều so với đoạn code viết trong hàm Update

Để biết thêm về những gì DOTween có thể làm được bạn có thể xem tài liệu trên trang chủ của DOTween

Xoay quanh trục hoặc tọa độ được chỉ định

Đôi khi bạn muốn xoay đối tượng xung quanh các trục tọa độ, trong trường hợp này bạn có thể sử dụng RotateAround

Sau đây chúng ta sẽ thử xoay nhân vật xung quanh trục z

Gắn đoạn code sau vào nhân vật

Sau khi chạy bạn sẽ có chuyển động giống như thế này:

Truy cập vào các đối tượng con

Foreach

Giả sử bạn có hệ thống hierarchy giống như sau:

Nếu bạn muốn duyệt toàn bộ các đối tượng con cấp 1 của root bạn có thể sử dụng foreach

Như thế này:

Dưới đây là kết quả in ra console

Thoạt nhìn transform không phải một array hay list nên tại sao nó có thể sử dụng được với foreach?

Vì transform kế thừa interface IEnumerable nên nó có thể duyệt được bằng foreach.

GetChild

Ngoài cách này bạn có thể sử dụng phương thức GetChild tham số truyền vào là index của đối tượng con mà bạn muốn lấy.

Lưu ý rằng ví dụ trên Root có 4 đối tượng con nên đối tượng con cuối cùng sẽ có index là 3 (tương ứng với childCount - 1) nếu bạn cố gắng lấy các đối tượng có index lớn hơn 3 một ngoại lệ sẽ được ném ra

Lấy toàn bộ các đối tượng con

Bằng cách sử dụng GetComponentsInChildren bạn có thể lấy được tất cả cấu trúc hierarchy bao gồm cả bản thân nó (self)

Ví dụ áp dụng với cấu trúc hierarchy sau

Kết quả hiển thị ra console:

Nếu bạn chỉ muốn nhận các đối tượng con mà loại bỏ chính bản thân mình hãy làm như sau

IsChildOf

Kiểm tra xem đối tượng Transform đã cho có phải là parent hay không?

Lúc này bạn có thể sử dụng IsChildOf cú pháp khá đơn giản như sau

Lưu ý rằng IsChildOf vẫn trả về true khi kiểm tra với chính bản thân nó

LossyScale

Đôi khi bạn muốn lấy scale chính xác của một đối tượng nằm sâu bên trong hierarchy

Như bạn thấy localScale của cả 2 Hina đều là 1 nhưng kích thước thật sự của chúng khác nhau. Bạn có thể dễ dàng lấy tỉ lệ chính xác bằng cách sử dụng lossyScale

Performance

Transform được sử dụng xuyên suốt quá trình phát triển trò chơi và có mặt ở khắp nơi trong code của bạn. vì vậy một thay đổi nhỏ về cách sử dụng transform cũng sẽ ảnh hưởng đến hiệu suất tổng thể của cả trò chơi

Cache transform

Cách mà bạn vẫn thường sử dụng để truy cập thuộc tính transform thường là gameObject.transform hoặc transform. Nhìn thì có vẻ chúng ta đang truy cập trực tiếp nó giống như bạn gọi tới GetComponent

Vì vậy bạn luôn GetComponent mỗi lần bạn truy cập vào transform, sẽ không có vấn đề gì nếu tần suất truy cập thấp. Tuy nhiên đôi khi bạn sẽ quên mất nó và sử dụng bên trong Update nơi mọi thứ cập liên tục mỗi frame. Lúc này CPU của bạn luôn phải GetComponent để biết được đối tượng transform mặc dù hai đứa đã làm quen với nhau từ frame trước đó.

trong trường hợp này lưu vào bộ nhớ cache là một giải pháp tuyệt vời

Ví dụ khi không sử dụng bộ nhớ đệm mã của bạn trông giống như thế này

Khi sử dụng bộ nhớ đệm

Sau khi transform được vào cache ở hàm Start, khi bạn truy cập biến _cacheTransform bạn sẽ có nó ngay lập tức mà không cần tốn bất kỳ chi phí phát sinh thêm nào.

Thực tế là transform và gameObject.transform không sử dụng GetComponent. Bên dưới Unity họ đã tạo ra một chức năng riêng được tối ưu hóa cho việc lấy Transform nó sẽ nhanh hơn việc sử dụng GetComponent. Tất nhiên bạn vẫn có thể sử dụng GetComponent trong các trường hợp bất khả kháng khác.

Tuy nhiên cho dù nó được tối ưu cỡ nào đi nữa nó vẫn chậm hơn so với việc truy cập từ bộ nhớ đệm.

Tóm lại cách tốt nhất để truy cập transform đó là lưu nó vào bộ nhớ đệm sau đó truy cập nó từ bộ nhớ đệm.

Thay vị trí và góc xoay cùng lúc

Trong trường hợp bạn thay đổi position và rotation cùng một lúc, bạn có thể sử dụng SetPositionAndRotation thay vì viết riêng và cập nhật riêng biệt từng thuộc tính nó sẽ mang lại hiệu suất tốt hơn

Performance cho hierarchy

Nếu độ sâu của hierarchy cố định, bạn có thể chỉ định độ tối đa thông qua hierarchyCapacity để có thể mong đợi cải thiện hiệu suất

Ví dụ bạn chỉ định độ sâu tối đa là 10

Nếu vượt quá 10 sẽ xuất hiện cảnh báo và trò chơi vẫn chạy

Theo như Unity nói nó có thể giảm mức sử dụng bộ nhớ và cải thiện hiệu suất của Transform.SetParentObject.Destroy Nhưng với độ sâu 10 hay 20 chúng không có gì khác nhau

Bạn có thể xem thêm tài liệu về hierarchyCapacity trên trang chủ Unity

Hoặc một bài blog nói về tối ưu hierarchy của unity

Performance giữa position và localPosition

Theo dõi đoạn code sau đây

Kiểm tra trong hai trường hợp có độ sâu hierarchy khác nhau ta có kết quả sau:

Với độ sâu là 6:

localPosition mất 56,57 ms trong khi đó positon là 81,71ms

Với độ sâu là 3

localPosition mất 57,22 ms trong khi đó positon là 67.77ms

Điều này có nghĩa là độ sâu càng lớn position càng chậm do phải tính toán nhiều transform

Thông số hiện thị trong inspector của Transform

Position hiển thị trong Inspector là tọa độ tương đối (tọa độ local) Rotation hiển thị trong Inspector là góc so với parent, mặc dù ghi là rotation nó là giá trị hiển thị của EulerAngle không phải của Quaternion

Performance giữa GetComponent vs transform

transform nhanh hơn so với GetComponent

Không di chuyển Transform trong FixedUpdate

Đơn giản là vì FixedUpdate không gọi liên từng frame như Update mà nó được giọi cố định sau mỗi 0.02 giây.

Giả sử ta có FPS (frame per sec) là 30 nghĩa là màn hình sẽ được cập nhật 30 lần mỗi giây. Trong trường hợp trò chơi có FPS là 30 thì mỗi frame sẽ là 0.03 giây. Khi đó Update sẽ được gọi sau mỗi 0.03 giây. Còn với FixedUpdate nó gọi sau mỗi 0.02 giây vậy nếu Update gọi được 2 lần thì FixedUpdate đã được gọi 3 lần

Bạn có thể xem chỉ số FixedTimeStep trong ProjectSetting

Vì FixedUpdate trục thời gian khác với FPS nên nếu di chuyển Transform với FixedUpdate có thể sẽ dẫn đến chuyển động không mong muốn. Đôi khi bạn tính toán quá trình cần được di chuyển bởi một frame lại chạy hai lần và quá trình di chuyển sẽ không đồng bộ.

Hãy chỉ sử dụng FixedUpdate cho các tính toán vật lý

Lượt nghé thăm