Lang thang trên trang chủ của cocos2d-x gặp được 1 bài TUT về Parallax Crolling khá hay, mình mang về Blog này để mọi người cùng ngâm cứu. Có thể nhiều bạn sẽ nghĩ mình đang "ăn cắp" bài viết của người khác làm bài của mình. Oh no, biết nói thế nào nhỉ, ngay từ ban đầu blog ( bài 1,2 gì đó) mình đã nói rõ, Code, Resource trong Blog này mình ko hề Code ra, mà chỉ nhặt nhạnh trên mạng, rồi đem về trình bày và diễn giải lại trên Blog để tự học và mọi người cùng đọc. Có nhiều bài mình thực hành code xong viết lại thành TUT, hoặc có 1 vài bài mình lấy Tut người khác, nhưng sẽ trình bày lại theo cách hiểu của mình sao cho dễ hiểu và gần gũi với cả người mới bắt đầu tìm hiểu về Engine, và đây là 1 bài như thế. Sẽ không có gì quá đáng phải không, mà thôi, ai đánh giá thế nào thì kệ chứ, việc của chúng ta là HỌC, là NGÂM CỨU làm game chứ ko phải VIẾT SÁCH, VIẾT TUT!
OK, trước tiên chúng ta cùng tìm hiểu 1 hiện tượng vật lý cực kỳ hay gặp trong cuộc sống, đó là Parallax Scrolling
Parallax Scrolling là gì? Dùng trong trường hợp nào?
Từ Parallax có nghĩa là Thị sai trong tiếng Việt đó
Bạn gõ Parallax hoặc Thị sai lên Google là sẽ có kết quả trả về lập tức, đây là trang Wiki
Hiện tượng Parallax Scrolling là hệ quả được tạo ra bởi thị sai ( Parallax ) của mắt với các vật thể xa, gần, Vật càng xa, góc nhìn càng nhỏ, vật càng gần thì góc nhìn càng rộng, nêu khi 2 vật xa gần di chuyển cùng 1 vận tốc ( hoặc khác vận tốc ), thì ta sẽ thấy vật ở gần (có vẻ ) di chuyển nhanh hơn vật ở xa.
Chúng ta có thể hiểu Hiệu ứng Parallax Scrolling là hiệu ứng các vật chuyển động tương đối với vận tốc tỷ lệ nghịch với khoảng cách từ vật tới mắt người quan sát, Vật gần di chuyển nhanh, vật xa di chuyển chậm
Ví dụ về Prallax trên HTML5
Ví dụ trong cuộc sống hàng ngày
+ Khi bạn đi tàu, xe, nhìn ra ngoài cửa sổ sẽ thấy, Cột điện "lùi lại rất nhanh", cánh đồng "lùi lại vừa phải", Mây "lùi lại chậm" Mặt trời "lùi lại rất chậm". Nghĩa là vật càng gần mắt người quan sát thì chuyển động càng nhanh, càng ở xa thì chuyển động càng chậm. ( mặc dù các vật trên có thể coi là cùng vận tốc - đứng im - so với mắt người đang đứng im ) đừng ai bắt bẻ về "sự đứng im" và chuyển động nhé. cũng chỉ tương đối thôi, quan trọng là mắt người quan sát thấy thế nào thôi.
Ứng dụng trong game:
Hiệu ứng Parallax Scrolling hay dùng trong game 2D để tạo chiều sâu cho game, bạn hãy vào trang web bên trên trải nghiệm nhé,
Tạo hiệu ứng này như thế nào?
Dựa vào định nghĩa trên, về lý thuyết thì ta có thể làm như sau trong game
+ Ta tạo ra Các layer chồng lên nhau
+ Layer nào ta định nghĩa ở xa thì ta cho dịch chuyển với tốc độ chậm
+ Layer nào định nghĩa ở gần thì ta dịch chuyển với tốc độ nhanh hơn
+ Các vận tốc này thường cùng phương cùng chiều, độ lớn tỷ lệ với khoảng cách đến mắt quan sát
Lý thuyết có vẻ đơn giản, thực tế Code thì sao?
Chúng ta cùng bắt đầu nào!
Chúng ta cùng bắt đầu nào!
Bạn tạo 1 Project mới đặt tên Parallax
Xây dựng Class InfiniteParallaxNode như sau
InfiniteParallaxNode.h
class InfiniteParallaxNode : public ParallaxNode
{
public:
static InfiniteParallaxNode* create(); // Tạo InfiniteParallaxNode
void updatePosition(); // Update lại vị trí
};
InfiniteParallaxNode.cpp
class PointObject : public Ref // Dựng 1 lớp PointObject
{
public:
// Các hàm inline thiết lập, và lấy về các thuộc tính
inline void setRation(Point ratio) {_ratio = ratio;}
inline void setOffset(Point offset) {_offset = offset;}
inline void setChild(Node *var) {_child = var;}
inline Point getOffset() const {return _offset;}
inline Node* getChild() const {return _child;}
private:
Point _ratio; // Tỉ lệ
Point _offset; // Độ lệch
Node* _child; // Node con
};
InfiniteParallaxNode* InfiniteParallaxNode::create()
{
// Tạo mới 1 đối tượng InfiniteParallaxNode*
InfiniteParallaxNode* node = new InfiniteParallaxNode();
if(node) {
node->autorelease();
} else {
delete node;
node = 0;
}
return node;
}
void InfiniteParallaxNode::updatePosition()
{
int safeOffset = -10;
Size visibleSize = Director::getInstance()->getVisibleSize();
// 1. Duyệt các con của chuỗi parallax
for(int i = 0; i < _children.size(); i++)
{
auto node = _children.at(i); // Node i
// 2. Kiểm tra node đó có ra ngoài màn hình ko, convertToWorldSpace bạn tham khảo tại đây http://www.cocos2d-x.org/wiki/Coordinate_System.
if(convertToWorldSpace(node->getPosition()).x + node->getContentSize().width < safeOffset)
// 3. Tìm PointObject tương ứng với node hiện tại
for(int j = 0; j < _parallaxArray->num; j++)
{
auto po = (PointObject*)_parallaxArray->arr[j];
// Nếu có thì tăng thêm độ lệch ra thêm 1 màn hình, tức xuất hiện lại thêm lần nữa
if(po->getChild() == node)
po->setOffset(po->getOffset() +
Point(visibleSize.width + node->getContentSize().width,0));
}
}
}
Trong file HelloWorldScene.h
Thêm include
#include "InfiniteParallaxNode.h"
InfiniteParallaxNode* _backgroundElements; // 1 con trỏ InfiniteParallaxNode
float randomValueBetween(float low, float high); // random trong 1 khoảng
void update(float delta);
Trong file HelloWorldScenecpp
Phần Include
#include <climits>
#include <cstdlib>
#include <iostream>
using namespace std;
void HelloWorld::update(float delta)
{
Point scrollDecrement = Point(5, 0); // Tốc độ Scroll, càng lớn cuộn càng nhanh
_backgroundElements->setPosition(_backgroundElements->getPosition() - scrollDecrement);
_backgroundElements->updatePosition();
}
// Giá trị ngẫu nhiên trong 1 khoảng
float HelloWorld::randomValueBetween(float low, float high)
{
return (((float) rand() / RAND_MAX) * (high - low)) + low;
}
Hàm init()
// Tạo màu nền cho background
auto backgroundColor = LayerColor::create(
Color4B(173, 255, 250, 255),
visibleSize.width,
visibleSize.height);
backgroundColor->setPosition(Point::ZERO);
addChild(backgroundColor, 0);
// Ảnh Background
auto ground = Sprite::create("ground.png");
ground->setAnchorPoint(Point(0,0));
ground->setPosition(Point::ZERO);
addChild(ground, 1);
// Tạo 1 đối tượng Parallax
_backgroundElements = InfiniteParallaxNode::create();
// Thêm 7 đối tượng (núi tuyết) vào Parallax
unsigned int rocksQuantity = 7;
for(unsigned int i = 0; i < rocksQuantity; i++)
{
auto rock = Sprite::create("rock.png");
rock->setAnchorPoint(Point::ZERO);
// Đặt độ lớn theo tỷ lệ ngẫu nhiên
rock->setScale(randomValueBetween(0.6, 0.75));
// Thêm vào Parallax
_backgroundElements->addChild(rock,
// Z-index, index lớn thì nằm chồng lên index nhỏ
randomValueBetween(-10, -6),
// Tỉ lệ tốc độ ( sẽ nhân với tốc độ Paralax)
Point(0.05, 1),
// Đặt vị trí của đối tượng, các đối tượng sẽ đặt cách nhau 1 khoảng tính theo công thức như bên dưới
Point(
(visibleSize.width / 5) * (i + 1) + randomValueBetween(0, 100),
ground->getContentSize().height - 5));
}
// Thêm 35 cái cây vào Parralax
unsigned int treesQuantity = 35;
for(unsigned int i = 0; i < treesQuantity; i++)
{
auto tree = Sprite::create("tree.png");
tree->setAnchorPoint(Point::ZERO);
tree->setScale(randomValueBetween(0.5, 0.75));
_backgroundElements->addChild(
tree,
randomValueBetween(-5, -1),
Point(0.5, 1),
Point(visibleSize.width / (treesQuantity - 5) * (i + 1) + randomValueBetween(25,50),
ground->getContentSize().height - 8));
}
// Thêm Parallax vào :Layer, update Scene
addChild(_backgroundElements, 2);
schedule(schedule_selector(HelloWorld::update), 0.01);
// Thêm cha mặt trời vào
auto sun = Sprite::create("sun.png");
sun->setAnchorPoint(Point(0.5,0.5));
sun->setScale(0.25);
sun->setPosition(Point(visibleSize.width * 2.0 / 3, visibleSize.height * 6.0 / 7));
addChild(sun, 3);
Một số hạn chế:
+ Bạn phải ước lượng tỉ lệ vận tốc giữa các "Layer" Parallax ( vì thực sự đâu có nhiều Layer đâu - Chỉ có 1 Paralax ) do không biết được "khoảng cách" giữa các lớp này.
+ Khi mô phỏng bạn làm sao cho các đối tượng gần thì chuyển động nhanh, các đối tượng xa thì chuyển động chậm hơn. Vậy thôi.
Dowload Resource, Code
Dowload Resource, Code
Mình kết thúc bài này ỏ đây nhé, hẹn gặp lại trong các bài sau
Bài 26 : Học làm game 4 - Đập chuột ( Part 1 )
Thank chủ thớt nhiều :)
ReplyDeleteBạn ơi mình làm nó bị lỗi mình sửa hoài mà ko được nhờ bạn xem giúp mình với, lỗi nó thế này:
ReplyDelete**************************************************
1>HelloWorldScene.obj : error LNK2019: unresolved external symbol "public: stati
c class InfiniteParallaxNode * __cdecl InfiniteParallaxNode::create(void)" (?cre
ate@InfiniteParallaxNode@@SAPAV1@XZ) referenced in function "public: virtual boo
l __thiscall HelloWorld::init(void)" (?init@HelloWorld@@UAE_NXZ)
1>HelloWorldScene.obj : error LNK2019: unresolved external symbol "public: void
__thiscall InfiniteParallaxNode::updatePosition(void)" (?updatePosition@Infinite
ParallaxNode@@QAEXXZ) referenced in function "public: virtual void __thiscall He
lloWorld::update(float)" (?update@HelloWorld@@UAEXM@Z)
1>D:\CCPRO\parallax\proj.win32\Debug.win32\parallax.exe : fatal error LNK1120: 2
unresolved externals
========== Build: 0 succeeded, 1 failed, 3 up-to-date, 0 skipped ==========
Error running command, return code: 1
bài này rất hay
ReplyDeletetks you! Bài viết rất hữu ích
ReplyDeletetrời,copy code với resoure chạy thử mà lỗi tung chảo,tìm k ra.chán k muốn code
ReplyDeletechỉ copy + pase thì ... =)) ăn sẵn nhưng cũng phải biết cách ăn cho đúng bạn ơi =))
Delete