5/01/2014

ProjectGame 1.3 - Tạo class CharacterController và SpriteManager




Trong bài viết này, chúng ta tạo file Javascript mới để điều khiển việc di chuyển của nhân vật và các ảnh chuyển động cho từng động tác của nhân vật. Chúng ta có thể code bằng Unitron (Mac), UniSciTE (Windows), hoặc MonoDevelop. MonoDevelop là công cụ biên tập chính được thiết kế cho code C# và môi trường .NET, vì vậy nếu bạn quen C# hơn bạn có thể yêu thích nó. Tuy nhiên, chúng ta sẽ code bằng JavaScript bởi vì nó có nhiều hàm nhanh hơn và debug tốt hơn, như tìm hoặc thay thế từ trong toàn bộ project bằng cách nhấn tổ hợp phím Command + Shift + Fin (Mac) hoặc Control + Shift + Fin (Windows) và tự động hoàn thành từ tìm kiếm. Nếu bạn đã biết JavaScript thì việc chuyển sang C# khá dễ dàng.


Mục tiêu

1.1 - Tạo Camera và Level
1.2 - Tạo nhân vật 2D
>> 1.3 - Tạo class (lớp) CharacterController và SpriteManager
1.4 - Nhảy và yếu tố vật lý
1.5 - Tạo chìa khóa và mở cửa
1.6 - Chèn âm thanh và nút Replay


Chuẩn bị

Bây giờ, chúng ta sẽ bắt tay vào code, nhưng trước tiên, chúng ta phải thiết lập lại đôi chút.

B1. Tại thẻ Project, vào thư mục Scripts và xóa tất cả các file đi.

B2. Tiếp theo, chúng ta phải thiết lập lại Unity để sử dụng MonoDevelop như trình biên tập code mặc định. Bằng cách vào Edit | Preferences.

B3. Chúng ta sẽ thấy các mục thiết lập cho Unity trong cửa sổ vừa hiện lên. Trong tab General, vào External Script Editor và thay đổi Use build-in editor thành MonoDevelop bằng cách nhấp chọn nút Browse và chọn tìm đến thư mục cài đặt Unity và chọn Unity | MonoDevelop | MonoDevelop.exe.


MonoDevelop là một "Môi trường phát triển tích hợp" (IDE) cơ bản được thêm vào từ Unity 3.X, nó có môi trường code và debug tốt hơn. Chúng ta có thể tìm hiểu thêm về cách điều chỉnh qua trang web của Unity:

http://unity3d.com/support/documentation/Manual/HOWTO-MonoDevelop.html


Bắt tay vào làm việc

B1. Tại thẻ Project, nhấp phải vào thư mục Scripts và chọn Create | JavaScript và đặt tên là CharacterController2D.



B2. Double click vào file JavaScript CharacterController2D để mở file này lên và bắt đầu code.

B3. Trước tiên chúng ta sẽ tiến hành khai báo các biến cần thiết như sau:

public var f_speed : float = 5.0;
public var loopSprites : SpriteManager[];
private var in_direction : int;

Ở đây, biến f_speed biểu thị cho tốc độ của nhân vật và chúng ta thiết lập nó là public vì vậy chúng ta co thể tùy chỉnh nó trong thẻ Inspector ở Unity Editor. Mãng loopSprite của lớp SpriteManager sẽ kiểm soát quá trình cập nhật texture của sprite khi chuyển động, chúng ta sẽ tạo ra lớp này sau. Biến in_direction lưu hướng của nhân vật, biến này chỉ mang 2 giá trị là 1 (sprite kế tiếp bên phải) và -1 (sprite kế tiếp bên trái).

B4. Tiếp theo, chúng ta sẽ bổ sung code sau vào hàm Start( ) đã mặc định được tạo sẵn.

public function Start() : void {
       in_direction = 1;
       //Initialization Sprite Manager
       for (var i : int = 0; i<loopSprites.length; i++) {
              loopSprites[i].init();
       }
       //Update Main Camera to the character position
       Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y,Camera.main.transform.position.z);
}

B5. Tiếp theo, chúng ta sẽ bổ sung đoạn code sau vào hàm Update( ) cũng đã được mặc định tạo sẵn:

// Update is called once per frame
public function Update () : void {
       if (Input.GetButton("Horizontal")) {
              //Walking
              in_direction = Input.GetAxis("Horizontal") < 0 ? -1: 1;
              rigidbody.velocity = new Vector3((in_direction*f_speed), rigidbody.velocity.y, 0);
              //Reset Stay animation frame back to the first frame
              loopSprites[0].resetFrame();

              //Update Walking animation while the character is walking
              loopSprites[1].updateAnimation(in_direction, renderer.material);
       } else {
              //Stay
              //Reset Walking animation frame back to the first frame
              loopSprites[1].resetFrame();
              //Update Stay animation while the character is not walking
              loopSprites[0].updateAnimation(in_direction, renderer.material);
       }
}

B6. Sau đó chúng ta sẽ tạo thêm hàm LateUpdate( ) phía bên dưới hàm Update( ). Chúng ta sẽ sử dụng hàm này để cập nhật vị trí camera sau khi nhân vật di chuyển bằng cách điều chỉnh tọa độ để theo sau nhân vật.

public function LateUpdate() : void {
       //Update Main Camera
       Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y, Camera.main.transform.position.z);
}


B7. Kế tiếp, chúng ta sẽ tạo lớp SpriteManager để quản lý các ảnh chuyển động của chúng ta trong file JavaScript CharacterController2D vừa tạo ở các bước trên. Tiếp tục bổ sung đoạn code sau vào bên dưới hàm LateUpdate( ) trong file script chúng ta đang làm.
class SpriteManager {
       public var spriteTexture : Texture2D; //Set Texture use for a loop animation such as walking, stay, etc.
       public var in_framePerSec : int; //Get frame per sec to calculate time
       public var in_gridX : int; //Get max number of Horizontal images
       public var in_gridY : int; //Get max number of Vertical images
       private var f_timePercent : float;
       private var f_nextTime : float; //Update time by using frame persecond
       private var f_gridX : float;
       private var f_gridY : float;
       private var in_curFrame : int;
     
       public function init () : void {
              f_timePercent = 1.0/in_framePerSec;
              f_nextTime = f_timePercent; //Update time by using frame persecond
              f_gridX = 1.0/in_gridX;
              f_gridY = 1.0/in_gridY;
              in_curFrame = 1;
       }

       public function updateAnimation (_direction : int, _material : Material) : void {
              //Update material
              _material.mainTexture = spriteTexture;
              //Update frame by time
              if (Time.time>f_nextTime) {
                     f_nextTime = Time.time + f_timePercent;
                     in_curFrame++;
                     if (in_curFrame>in_framePerSec) {
                            in_curFrame = 1;
                     }
              }
              _material.mainTextureScale = new Vector2 (_direction * f_gridX, f_gridY);
              var in_col : int = 0;
              if (in_gridY>1) {
                     //If there is more than one grid on the y-axis update the texture
                     in_col= Mathf.Ceil(in_curFrame/in_gridX);
              }
              if (_direction == 1) { //Right
                     _material.mainTextureOffset = new Vector2(((in_curFrame)%in_gridX) * f_gridX, in_col*f_gridY);
              } else { //Left
                     //Flip Texture
                     _material.mainTextureOffset = new Vector2(((in_gridX + (in_curFrame)%in_gridX)) * f_gridX, in_col*f_gridY);
              }
       }

       public function resetFrame () :void {
              in_curFrame = 1;
       }
}

B8. Bây giờ, ấn Ctrl + S để save file script này lại, kéo thả nó vào Player ở thẻ Hierarchy. Nhấp chọn Player và qua thẻ Inspector điều chỉnh Size = 2 trong Loop Sprites và các mục khác như sau:


B9. Save scene lại, và ấn nút Play để kiểm tra thành quả. Bạn hãy ấn phím mũi tên qua trái / qua phải hoặc nút A / D để di chuyển qua trái hoặc qua phải.


Hoàn thành mục tiêu 1.3

Chúng ta vừa tạo một đoạn script để kiểm soát chuyển động của và xử lý các động tác của nhân vật. Trước tiên chúng ta thiết lập biết in_direction là 1 bởi vì chúng ta muốn nhân vật lúc bắt đầu sẽ quay mặt sang bên phải. Khi đó, mảng hình ảnh sẽ được lặp và khởi tạo trong lớp SpriteManager tùy theo độ dài mà chúng ta đặt. Chúng ta sẽ sử dụng main camera từ scene hiện tại thông qua cú pháp Camera.main. Cú pháp này cho phép ta truy cập đến đối tượng Main Camera từ bất kì đâu chúng ta muốn và khi đó chúng ta hướng vị trí của camera theo nhân vật. Tiếp theo, chúng ta sẽ đặt đoạn code vào hàm Update( ) đã được mặc định tạo sẵn, tương tự như hàm Start( ). Hàm này sẽ điều khiển quá trình chuyển động khi nhân vật từ bước đi sang nhảy và cập nhật các ảnh chuyển động.

Sau đó, chung ta sử dụng lớp Input để phát hiện khi người chơi nhấn phím bấm từ bàn phím. Chúng ta làm tất cả việc điều khiển nhân vật trong hàm Update( ). Đầu tiên, chúng ta sử dụng if (Input.GetButton("Horizontal")) { } để kiểm tra việc người chơi đã nhấn phím Horizontal (mặc định trong Unity là A, D, ←, →) và chúng ta di chuyển nhân vật nếu phím được nhấn. Dòng đầu tiên của hàm if kiểm tra hướng của nhân vật in_direction =Input.GetAxis("Horizontal") < 0 ? -1: 1; có nghĩa là nếu nhấn nút Horizontal, chúng ta sẽ lấy giá trị tọa độ từ hàm Input.GetAxis("Horizontal"). Hàm Input.GetAxis sẽ trả về giá trị từ -1 đến 1 biểu thị cho việc người chơi đang nhấn nút. Sau đó, chúng ta kiểm tra giá trị có nhỏ hơn 0 hay không. Nếu có thì hàm trả về -1 (di chuyển qua trái), nếu không thì trả về 1 (di chuyển qua phải). Khi đó dòng rigidbody.velocity = new Vector3((in_direction*f_speed), rigidbody.velocity.y, 0); chúng ta đã gán hướng và tốc độ vào rigidbodyvelocity. Chúng ta không gán giá trị cho trục Z bởi vì nhân vật của chúng ta không di chuyển theo hướng đó.

Cuối cùng, chúng ta tạo lớp SpriteManager cho file Javascript của chúng ta để điều khiển việc xử lý các vật liệu gán cho sprite bằng cách sử dụng tối đa số bức ảnh chúng ta đã tính với thời gian khi chạy mỗi bức ảnh. Hãy nhìn qua lớp SpriteManager. spriteTexture cơ bản dùng để thiết lập vật liệu của các sprite được lấy từ class. Những texture này sẽ được gọi và gán vào vật liệu chính khi nhân vật thay đổi di chuyển, như từ bước đi sang đứng yên, bước đi sang nhảy và vân vân.... in_framePerSec là tổng số ảnh texture của sprite, sẽ được tính để hiển thị trong khung hình kế tiếp. in_gridX là số dòng của sprite texture, và in_gridY là số cột, để tính toán Tiling và Offset của texture như chúng ta đã tìm hiểu ở bài viết trước. Chúng ta thiết lập private cho các biến f_timePercent, f_nextTime, f_gridX, f_gridY, và in_curFrame, mà chúng ta sử dụng để tính toán trong hàm updateAnimation( ). Tiếp theo, chúng ta có hàm init( ). Hàm này cơ bản là thiết lập các biến. Sau đó, hàm updateAnimation( ) sẽ lấy vật liệu và hướng nhìn của nhân vật để xử lý và cập nhật các ảnh chuyển động. Cuối cùng, chúng ta có hàm resetFrame( ) để thiết lập lại số khung hình chuyển động về 1.


Kiến thức bổ sung

Input Manager


Trong Unity, chúng ta có thể tùy chỉnh thiết lập Input Manager bằng cách vào Edit | Project Settings | Input. Trong thẻ Inspector, nhấp chọn Axes và bạn sẽ thấy Size: 17, là độ dài mảng của tất cả các input. Nếu có hơn 17 input, chúng ta có thể nhập số vào đây (mặc định là 17). Tiếp theo chúng ta sẽ thấy có 17 tên từ Horizontal đến Jump như thiết lập mặc định. Mỗi cái sẽ có các biến riêng chúng ta có thể điều chỉnh.

Chúng ta có thể thông tin thêm tại website của Unity:

http://unity3d.com/support/documentation/Components/classInputManager.html

Trong file script của chúng ta, chúng ta sử dụng hàm Input.GetButton("Horizontal"). GetButton dùng để kiểm tra nếu các nút Horizontal được nhấn hay không. Horizontal là tên của input đầu tiên như chúng ta đã thấy ở ảnh minh họa bên trên. Chúng ta cũng có thể dùng Input.GetKey("left") để làm việc đó. Nó cũng có kết quả tương tự với Input.GetButton, nhưng khác nhau là GetKey chỉ phát hiện khi phím chỉ định được nhấn. Vì vậy GetButton dễ cho việc người dùng thay đổi phím trong suốt quá trình chơi. 2 nút Negative Button và Positive Button sẽ gửi giá trị âm và dương, thích hợp cho việc điều hướng như lên, xuống, trái, phải. Ngoài ra còn có biến Dead, biến này sẽ thiết lập bất kì số nào nhỏ hơn từ giá trị của biến này đến 0, rất thích hợp cho việc sử dụng joystick. Cũng có thể điều chỉnh Type thành key/mouse và bật biến Snap để khi nhận được giá trị đối nghịch tọa độ sẽ trả về 0.


2 comments: