Search…

Tạo Hiệu Ứng Sét Đánh (Cocos2d-x 2.x.x)

27/09/20203 min read
Hướng dẫn tạo ra hiệu ứng đó hiệu ứng sấm sét dây chuyền trong Cocos2d-x

Game nói chung và game di động nói riêng hấp dẫn người chơi không chỉ nhờ vào nội dung hấp dẫn mà còn dựa vào những hiệu ứng đồ hoạ, âm thanh sống động. Bài viết sẽ hướng dẫn tạo ra hiệu ứng đó hiệu ứng sấm sét dây chuyền.

ss_1

Phân tích vấn đề

Sau khi hoàn thành bài viết này, có thể tạo ra hiệu ứng như sau:

Hiệu ứng sét đánh Cocos2d-x

Và tất nhiên rằng, hiệu ứng sấm sét sẽ chớp giật liên tục chứ không tĩnh như hình vẽ thấy ở trên. Để tạo ra hiệu ứng như trên, sử dụng mẫu sét dưới đây. 

Hiệu ứng sét đánh Cocos2d-x

Ý tưởng là tạo hình sấm sét bằng cách ghép nhiều mẫu sét nhỏ để tạo thành một đường sét dài và vị trí của các mẫu sét được chọn ngẫu nhiên để hiệu ứng giống với thực tế hơn. Để hiểu rõ hơn, có thể theo dõi hình minh hoạ dưới đây:

Hiệu ứng sét đánh Cocos2d-x

Hiện thực

Để đơn giản trong quá trình hiện thực, tạo mẫu sét bằng cách chia đều hình ảnh thành 1 bảng có kích thước MxN.

  • M là số mẫu sét theo chiều đứng
  • N là số mẫu sét theo chiều ngang.

Hiệu ứng được khởi tạo bởi hình ảnh chứa các mẫu sét và 2 thông số kèm theo M, N. Như hình ảnh mẫu sét đã minh hoạ ở trên, M và N tương ứng có giá trị là 18.

static StormEfx* create(const char* textureName, int numRows, int numCols);

Hàm khởi tạo hiệu ứng:

void initStorm(CCPoint startPoint, CCPoint endPoint, float lightSpread,
			float boltLength, int numFramesPerChange, float endBoltLength);

Trong đó:

  • startPoint, endPoint: vị trí bắt đầu và kết thúc của hiệu ứng.
  • lightSpread: thông số xác định độ lan rộng sang 2 bên của hiệu ứng. Giá trị của lightSpread càng lớn, hiệu ứng sẽ các rộng.
  • boltLength: độ dài của mỗi mẫu sét khi vẽ lên màn hình.
  • numFramesPerChange: hình dạng của hiệu ứng sẽ được cập nhật sau mỗi numFramesPerChange frame.
  • endBoltLength: độ dài tối đa của mẫu cuối cùng.

Đoạn chương trình khởi tạo hiệu ứng:

CCTexture2D* pTex = CCTextureCache::sharedTextureCache()->addImage(textureName);

m_iNumTextureRows = numRows;
m_iNumTextureCols = numCols;

m_fTextureBoltLen = (float) pTex->getPixelsHigh() / m_iNumTextureRows;

float sampleWidth = pTex->getPixelsWide() / m_iNumTextureCols;
float sampleHeight = pTex->getPixelsHigh() / m_iNumTextureRows;

int numBoltSamples = m_iNumTextureRows*m_iNumTextureCols;
m_vSpriteBolts = new CCSprite*[numBoltSamples];

for(int i = 0; i < numBoltSamples; i++)
{
	int r = i / m_iNumTextureCols;
	int c = i % m_iNumTextureCols;

	m_vSpriteBolts[i] = CCSprite::createWithTexture(pTex, 
		CCRectMake(c*sampleWidth, r*sampleHeight, sampleWidth, sampleHeight));

	m_vSpriteBolts[i]->retain();
}

Mỗi mẫu sét sẽ được ánh xạ tương ứng với một đối tượng CCSprite, dùng những đối tượng này để tạo hình cho hiệu ứng.

Hiệu ứng sẽ được sinh ngẫu nhiên và liên tục:

float distToEnd = 0.0f;

CCPoint vectToEnd;
CCPoint curPosition = m_tStartPoint;

int idx = 0;

do
{
	m_vBolts[idx] = curPosition;

	vectToEnd = ccpSub(m_tEndPoint, curPosition);
	vectToEnd.y += randFloat(-m_fLightningSpread,m_fLightningSpread)*vectToEnd.x;
	vectToEnd.x += randFloat(-m_fLightningSpread,m_fLightningSpread)*vectToEnd.y;
	distToEnd = ccpLength(vectToEnd);
	vectToEnd = ccpNormalize(vectToEnd);
	vectToEnd = ccpMult(vectToEnd, m_fBoltLen);

	curPosition = ccpAdd(curPosition, vectToEnd);

	idx++;
}
while(distToEnd > m_fEndBoltLength);

m_vBolts[idx++] = m_tEndPoint;

m_iNumBolts = idx;

Các đối tượng CCSprite tương ứng với các mẫu sét sẽ được thiết đặt các thông số vị trí, góc quay tương ứng để tạo hình hiệu ứng đã sinh ở trên.

for(int i = 0; i < m_iNumBolts-1; i++)
{
	CCPoint start = m_vBolts[i];
	CCPoint end = m_vBolts[i+1];

	CCPoint pDirection = ccpSub(end, start);

	float rotatedAngle = ccpToAngle(pDirection);
	float degreeAngle = CC_RADIANS_TO_DEGREES(rotatedAngle);
	float boltLength = ccpLength(ccpSub(end, start));

	float scaleFactor = boltLength / m_fTextureBoltLen;

	int randomBoltIdx = CCRANDOM_0_1()*(m_iNumTextureRows*m_iNumTextureCols-1);
	CCSprite* pBolt = m_vSpriteBolts[randomBoltIdx];
	pBolt->setScaleY(scaleFactor);
	pBolt->setScaleX(scaleFactor*m_fBoltWidthScale);
	pBolt->setRotation(90-degreeAngle);
	// average position between start and end
	pBolt->setPosition(ccpMult(ccpAdd(start, end), 0.5f));
	pBolt->visit();
}

Cuối cùng, để tạo hiệu ứng sấm sét chéo màn hình như hình mình hoạ ở đầu bài viết, sử dụng đoạn mã nguồn sau:

CCSize winSize = CCDirector::sharedDirector()->getWinSize();

StormEfx* effect = StormEfx::create("efx_thunder.png", 1, 8);
effect->initStorm(ccp(0, 0), ccp(winSize.width, winSize.height), 0.5f, 48.0f, 15, 96.0f);
effect->setLifeTime(10.0f);
this->addChild(effect);

Kết quả 

efx_thunder_1

Code

Mã nguồn của component StormEfx: StormEfx.zip

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024