Monday, September 29, 2014

Cocos2d-x: Tìm hiểu các khái niệm cơ bản (Phần 1)


1 Khái niệm Node, Scene, Layer

- Node, Scene, Layer là 3 khái niệm cơ bản nhất và cũng khá mơ hồ nhất khi tiếp cận Cocos2d-x. Do vậy ở phần đầu chúng ta sẽ cùng tìm hiểu qua các khái niệm này như thế nào.

- Trước hết chúng ta tạm chấp nhận cấu trúc kế thừa các lớp






1.1 Node

- Node là khái niệm cơ bản và cốt lõi nhất của Cocos2d-x, Node được định nghĩa trong lớp Node. Thực ra đây là một lớp trừu tượng, nó không có một thể hiện trực tiếp cụ thể mà được thể hiện thông qua các lớp con của nó.

- Trong biểu đồ kế thừa toàn cảnh thì Node là đối tượng cơ sở nhất, các lớp con thông dụng thường kế thừa từ lớp Node là: Scene, Layer, Sprite…

- Các tính năng chính trong một Node là
  • Node có thể chứa các Node khác, nên trong một Node sẽ có các thao tác xử lý với các Node con như: addChild, getChildByTag, removeChild…
  • Node có thể đặt lịch định kỳ gọi lại (schedule, unschedule…) tính năng này hỗ trợ cho việc vẽ lại các đối tượng hoặc xử lý vòng lặp game sau này chúng ta tìm hiểu
  • Node có thể thực hiện các Action (runAction, stopAction,…), các Action chúng ta sẽ tìm hiểu sau này, nhưng trước mắt có thể hình dung chúng như kiểu di chuyển Node, quay, làm mờ…
- Trong Node có các thuộc tính cơ bản
  • position (mặc định là : x=0, y=0)
  • scale (mặc định là: x=1, y=1)
  • rotation (góc quay theo độ, theo chiều kim đồng hồ, mặc định là 0)
  • anchor (mặc định là: x=0, y=0)
  • contentSize (mặc định là: width=0, height=0)
  •  visible (mặc định là: true)
  • Các thuộc tính này chúng ta sẽ dần làm quen ở các phần sau

1.2 Sence

- Khi làm game, chúng ta sẽ làm quen với khái niệm Scene trong thiết kế game. Có thể tạm hiểu Scene là các cảnh game hoặc các “màn hình” game: splash, main menu, in-game… những “cảnh” với các đối tượng đồ họa được “vẽ” trên đó tạo ra các “màn hình game” mà ở mỗi scene sẽ có những mục đích, nhưng tính năng nhất định cho người chơi.

- Sence là một lớp con trực tiếp của lớp Node, Sence về mặt logic thì không khác gì Node, tuy nhiên nó có “nhiệm vụ” rõ ràng hơn Node, đó là nhiệm vụ mang các Node con để chúng có thể được thể hiện ra trên màn hình game. Các “Node con” đó là Layer và Sprite mà chúng ta sẽ tiếp tục tìm hiểu.

- Ở mức độ Sence, dù đã có nhiệm vụ rõ ràng hơn nhưng thật ra vẫn chỉ là chỗ chứa, chứ không tự nó hiện ra cho chúng ta thấy được, do vậy các tính năng và thuộc tính của Sence cũng không có gì khác với Node, ngoại trừ có thêm tính năng PhysicsWorld sẽ được áp dụng vào phần mô tả thế giới thực trong game.

inline PhysicsWorld* getPhysicsWorld() { return _physicsWorld; }

static Scene *createWithPhysics();

1.3 Layer

- Sence mang trên nó các đối tượng trong game: nhân vật, địch, background, mây, hoa… nhưng các đối tượng này không phải khi nào cũng cùng nhau ở trên 1 vùng logic. Có nghĩa là đôi khi chúng ta cần các đối tượng này tách biệt nhau về mặt logic, có thể đám mây sẽ trôi trong khi bụi hoa đang đứng yên, hoặc nếu người chơi game touch vào nhân vật thì sẽ nhảy lên chẳng hạn… Để làm được điều này, tách lớp các đối tượng game trong mỗi Sence, chúng ta thường dùng đến Layer.

- Ở đây ta nói thường dùng đến Layer là vì theo cấu trúc Node chưa được Node thì Sence hoặc Node đều có thể đóng vài trò chia lớp như Layer. Tuy nhiên, lớp Layer có thực thi giao thức tiếp nhận các sự kiện Touch hoặc Accelerometer (gia tốc kế), do vậy trong các trường hợp cần thiết như vậy chúng ta sẽ dùng Layer. Hay nói cách khác, chúng ta nên dùng Layer cho việc tách lớp trong Cocos2d-x

- Như vậy trong lớp Layer chúng ta sẽ có thêm các phương thức liên quan đến việc xử lý các sự kiện Touch, Accelerometer

virtual bool onTouchBegan(Touch *touch, Event *unused_event);
virtual void onTouchMoved(Touch *touch, Event *unused_event);
virtual void onTouchEnded(Touch *touch, Event *unused_event);
virtual void onTouchCancelled(Touch *touch, Event *unused_event);

virtual void onTouchesBegan(const std::vector<Touch*>& touches, Event *unused_event);
virtual void onTouchesMoved(const std::vector<Touch*>& touches, Event *unused_event);
virtual void onTouchesEnded(const std::vector<Touch*>& touches, Event *unused_event);
virtual void onTouchesCancelled(const std::vector<Touch*>&touches, Event *unused_event);

virtual void onAcceleration(Acceleration* acc, Event* unused_event);

2 Khái niệm Sprite

- Khi bắt tay vào lập trình game, ai trong chúng ta cũng sẽ đều được biết đến khái niệm rất cốt lõi, Sprite, nó được hiểu là một kiểu Texture, hay hơn hết là một ảnh 2D được sử dụng để vẽ lên màn hình game, hoặc nôm na là đối tượng nào tạo ra hình ảnh và hiển thị lên thì đó là Sprite.

- Sprite trong Cocos2d-x được thể hiện qua lớp Sprite, lớp này cũng được kế thừa trực tiếp từ Node và mang đầy đủ các tính năng của Node.



- Như vậy, đến đây phần nào chúng ta hiểu được vì sao lại gom nhóm các khái niệm Node, Sence, Layer với nhau và Sprite riêng 1 nhóm.
  • Sence và Layer là các Node dùng để chứa, để mang các Node khác
  • Và Sprite chính là các “Node khác” ở trên, Sence và Layer thường sẽ chứa các Sprite để tạo ra được một “màn hình”, một “cảnh” game



3 Quản lý tọa độ trong Cocos2d-x

3.1 Tọa độ gốc và quan hệ tọa độ cha-con giữa các node

- Trong Cocos2d-x, việc quản lý tọa độ của các Node theo nguyên tắc tham chiếu đến tọa độ của Node cha của nó. Tọa độ của Node con sẽ được tính từ tọa độ gốc của trục tọa độ Node cha, và tọa độ gốc đó là điểm Dưới-Trái của Node cha.


- Ở hình trên, ta giả sử Node cha trên cùng là Sprite hình ảnh nền của game. Khi đó tọa độ gốc (0, 0) của Sprite hình nền là ở góc Dưới-Trái như hình.

- Tiếp theo, ta thêm một Sprite con vào Sprite hình nền cha này để biết mối liên hệ tọa độ của Node con với Node cha như thế nào. Giả sử ta sẽ thêm một Sprite Angry Bird vào Sprite hình nền này. Sprite Bird có kích thước tạm chấp nhận là 200x200, chúng ta sẽ thêm Bird vào ở tọa độ (100,100) so với tọa độ gốc của Sprite hình nền cha.



- Qua hình ta nhận thấy, “node con” Bird sẽ thiết lập tọa độ (100,100) so với “node cha” dựa vào 2 điểm
  • Điểm tọa độ gốc của node cha, (0,0)
  • Điểm neo của bản thân node con (ở đây là điểm trọng tâm của node con), ta biết đến điểm này thông qua thuộc tính AnchorPoint của node con.
  •  Sprite Bird như vậy sẽ được xác định tọa độ của nó so với Sprite cha Hình nền theo nguyên tắc: điểm neo của Bird sẽ được đặt ở tọa độ (100,100) so với gốc tọa độ của node cha.

3.2 Điểm neo AnchorPoint

- Như vậy thì điểm neo AnchorPoint có vai trò như thế nào trong node
  • Đầu tiên như đã thấy ở trên, điểm neo Anchor nó thiết lập mối liên hệ tọa độ của node đó với cha của nó
  • Và thứ hai đó là điểm trung tâm cho phép quay của node

3.2.1 Anchor và tọa độ node

- Điểm AnchorPoint, các thành phần x,y của nó có giá trị nằm trong khoảng (0, 1). Mặc định, AnchorPoint có giá trị là (0.5, 0.5), tức là ở trọng tâm của node.



- Vẫn ví dụ ở trên, giờ ta chuyển điểm AnchorPoint thành (0,0) thay vì (0.5, 0.5) như điểm mặc định, khi đó kết quả sẽ là


3.2.2 Anchor và phép quay node

- Thiết lập điểm AnchorPoint của Bird lại thành (0.5, 0.5), tọa độ của Bird vẫn ở (100, 100) và thực hiện phép quay node 1 góc 300 theo chiều kim đồng hồ.


- Tiếp theo ta chuyển AnchorPoint thành (0,0) và thực hiện thao tác quay như trên



- Tiếp theo ta chuyển AnchorPoint thành (0,0) và tọa độ của Bird là (0,0) thay vì (100, 100) để vị trí của Bird trong lần này tương tự về mặt hình ảnh như vị trí trên, sau đó thử lại như trên






Cocos2d-x: Tìm hiểu các khái niệm cơ bản (Phần cuối)

No comments:

Post a Comment