SƯU TẦM CÁC THỦ THUẬT CỦA
IRRLICH ENGINE
------o0o---------
Ta có quy định một số biến chuẩn như sau :
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
//
Irr::IrrlichtDevice *device; //khởi tạo bằng hàm
CreateDevice hay CreateDeviceEx
//các biến quy định sau
Irr::video::IVideoDriver* driver = device->getVideoDriver();
Irr::scene::ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* env = device->getGUIEnvironment();
stringw caption = L“”; //Dùng để
hiện lên thanh tiêu đề của cửa sổ Irrlicht
1)
Hiển thị tốc độ khung hình và driver hiện tại :
int lastFPS = -1;
while(device->run())
{
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255,200,200,200));
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"driver[";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
else
device->yield();
}
2)
Lấy frameDelta time dùng cho chuyển động cố định bất chấp tốc độ
khung hình
u32 then = device->getTimer()->getTime();
// This is the movemen speed in units per second.
const f32 MOVEMENT_SPEED = 5.f;
while(device->run())
{
// Work out a frame delta time.
const u32 now = device->getTimer()->getTime();
const f32 frameDeltaTime = (f32)(now - then) / 1000.f;//Time in seconds
then = now;
3)
Cách dùng hàm : getCollisionPoint của class IsceneCollisionManager để
tìm kiếm va chạm đối với các SceneNode khác :
Cần có các biến thành phần :
line3df
ray;
ITriangleSelector
* selector; //cái nào tạo qua hàm CreateTriangleSelector() hay
createTriangleSelectorFromBoundingBox() của class IsceneManager
Các biến vector3df thì không cần bàn ( quá dễ)
Chú ý :
const ISceneNode
* outNode;
Lúc đó ta dùng hàm này như sau :
Smrg->getSceneCollisionManager()->getCollisionPoint (ray,
selector, outCollisionPoint, outTriangle,&outNode);
4)
Dùng custom font :
gui::IGUISkin* skin = env->getSkin();
gui::IGUIFont* font = env- >getFont("../../media/fonthaettenschweiler.bmp");
if (font) skin->setFont(font);
5)
Tạo ScreenShot được lưu theo thời gian lưu:
Dùng hàm sau đây :
void takeScreenshot(irr::IrrlichtDevice* device)
{
irr::video::IVideoDriver* const driver = device->getVideoDriver();
//get image from
the last rendered frame
irr::video::IImage*
const image = driver->createScreenShot();
if (image) //should
always be true, but you never know. ;)
{
//construct a
filename, consisting of local time and file extension
irr::c8 filename[64];
snprintf(filename,64,"screenshot_%u.png",device->getTimer()->getRealTime());
//write
screenshot to file
if
(!driver->writeImageToFile(image, filename))
device->getLogger()->log(L"Failed
to take screenshot.", irr::ELL_WARNING);
//Don't forget
to drop image since we don't need it anymore.
image->drop();
}
}
6)
Chuyển chuổi thành vector hay float hay int :
- Chuyển chuổi thành vector
vector3df
getVectorFromString(stringw str)\\
{
vector3df
vector;
swscanf(str.c_str(),
L"%f %f %f", &vector.X,&vector.Y,&vector.Z );
return
vector;
}
- Chuyển chuổi thành int
int
getIntegerFromString(stringw str)\\
{
int
integer;
swscanf(str.c_str(),L"%i",&integer);
return
integer;
}
- Chuyển chuổi thành dạng float
:
f32
getFloatFromString(stringw str)\\
{
f32
decimal;
swscanf(str.c_str(),L"%f",&decimal);
return
decimal;
}
7)
Chuyển Texture thành Image và ngược lại :
- Chuyển texture thành
image :
video::IImage* image =
driver->createImageFromData (
texture->getColorFormat(),
texture->getSize(),
texture->lock(),
false //copy mem
);
Còn cách khác ;
- Chuyển image thành
texture :
video::ITexture* texture
= driver->addTexture(name.c_str(),image);
texture->grab();
8)
Duyệt qua tòan bộ các SceneNode từ Node gốc:
core::array nodes;
smgr->getSceneNodesFromType(scene::ESNT_ANY,
nodes); // Find all nodes
for (u32 i=0; i < nodes.size(); ++i)
{
scene::ISceneNode * node = nodes[i];
[….]
}
Còn
một cách khác cũng hay là :
core::list::Iterator
begin = node->getChildren().begin();
core::list::Iterator
end = node->getChildren().end();
for (; begin != end; ++begin)
{
scene::ISceneNode * node = nodes[i];
[…..]
}
9)
Không cho hiện cửa sổ command window :
#pragma
comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
10) Hãm tốc độ khung hình theo yêu cầu (không dùng
Vsync do có thể có máy đặt Vsnyc khác nhau như 60 hay 65 hay 75…)
#include
#include
using namespace irr;
struct IrrlichtDelayFPS {
int
maxFPS;
int
lastFPS;
int
yieldCounter;
IrrlichtDevice *device;
IrrlichtDelayFPS() { maxFPS = 50;
yieldCounter = 0; device = 0; }
void
delay() {
video::IVideoDriver* driver = device->getVideoDriver();
int
lastFPS = driver->getFPS();
for(
int i = 0 ; i < yieldCounter ; i++ ) {
device->yield();
}
if (
lastFPS >maxFPS ) { yieldCounter++ ; }
else {
if
(yieldCounter > 0 ) { --yieldCounter; }
else
{ yieldCounter = 0 ; }
}
}
};
/* cách dùng
IrrlichtDelayFPS delayFPS;
delayFPS.maxFPS = 50; //set max FPS
delayFPS.device = device; //set device
while(device->run()) {
//your
code here
//delay
loop
delayFPS.delay();
}
*/
11) Can thiệp trực tiếp vào dữ liệu của Terrain :
Ta có các biến sau :
IMesh* mesh = terrain->getMesh();
IMeshBuffer* buffer = mesh->getMeshBuffer(0);
//chỉ cần mesh buffer 0 là đủ
S3DVertex2TCoords* data = (S3DVertex2TCoords*)buffer->
getVertices();
Giả sử ta có tọa độ vector3df pos(x,y,z) thuộc
terrain.
Để hiệu chỉnh đỉnh (vertex) tại vị trí này (ví dụ
tăng độ cao đỉnh này lên 10) ta làm như sau :
s32 scale = (s32)terrain->getScale().X
; // chú ý phải scale đầy đủ X, và Z bằng nhau
s32 size = heighmapImage->getDimension().Width;
// đây là biến lưu Image của HeightMap.
s32 x = (s32)(pos.X / scale);
s32 z = (s32)(pos.Z / scale);
s32 index = x * size + z;
Và sau củng ta có kết quả hiệu chỉnh như sau :
data[index].pos.Y += 10;
12) Hàm sinh số ngẫu nhiên tìm ở đâu trong
Irrlicht ?
Thật ra có đó. Trong tệp tin os.h có tất cả những cái bạn cần. Mình
trích ra như thế này ?
class Randomizer
{
public:
//!
resets the randomizer
static
void reset();
//!
generates a pseudo random number
static
s32 rand();
private:
static
s32 seed;
};
13) Dùng file nén và có mật khẩu trong Irrlicht :
Khi lập trình thì ai cũng muốn đưa sản phẩm của mình ra, nhưng phần
code và resource thì chắc chắn ai cũng muốn dấu rồi (kể cả mình).
Irrlicht đả có hàm để thực hiện chức năng này đó là hàm addFileArchive trong
filesystem có tham số như sau:
bool addFileArchive(const path&
filename, bool ignoreCase=true,bool ignorePaths=true,E_FILE_ARCHIVE_TYPE
archiveType=EFAT_UNKNOWN,
const core::stringc&
password="") =0;
+ filename : tên file nén (có thể có
password)
+ ignoreCase : bỏ qua chữ hoa hay chữ
thường (ví dụ : ant.x vàAnt.X cũng giống nhau)
+ ignorePaths : bỏ qua đường dẫn (bạn chỉ
cần cho tên là có thể tìm ra nó trong file nén của mình dù nó nằm trong các thư
mục khác nhau. Chú ý đều này)
+ archiveType : Irrlicht hỗ trợ các loại
file nén sau : pkzip, gzip,folder ảo, pak,npk,tar.
+ password : mật khẩu của tệp tin nén và
nó được mã hóa 256 bit.
* Mình dùng như
sau : dùng 7Zip nén toàn bộ resource của mình thành một tệp tin nén (loại zip)
có password mà mình định trước và theo chuẩn bzip2 và mã hóa theo theo chuẩn
AES-256. Vậy là mình tạm thời có thể yên tâm về resource mình kèm theo game rồi
!!
14) Dùng file âm thanh nén với
Audiere :
Mặc định Audiere không chấp nhận file nén, nhưng ta có cách khắc
phục như sau :
- // them phan
nay de chay file Zip
irr::io::IFileSystem* fs =
m_smgr->getFileSystem();
irr::io::IReadFile* rf =
fs->createAndOpenFile(filename);
if (!rf)
{
printf("**Not
found audio file: %s\n",filename);
return;
}
//
audiere::OutputStreamPtr
au_stream = 0;
//
if (zip_audio) ////dung file
audio nen
{
core::arraybuffer;
buffer.set_used(rf->getSize());
rf->read(buffer.pointer(),rf->getSize());
audiere::FilePtr
mf = audiere::CreateMemoryFile(buffer.pointer(),rf->getSize());
buffer.clear();
rf->drop();//close
file
au_stream =
audiere::OpenSound(m_audevice,mf,true,audiere::FF_AUTODETECT);
}
else //khong nen Audio
au_stream =
audiere::OpenSound(m_audevice,filename,true,audiere::FF_AUTODETECT);
15) Custom SceneNode hay Custom Animator có tham
chiếu với SceneNode khác :
Giả sử bạn tạo SceneNode trong đó có tham chiếu đến terrain hay
SceneNodeAnimator (dĩ niên là phải dính đến scenenode tham chiếu của nó rồi,
nhưng ngoài ra nó còn tham chiếu đến địa hình) thì khi lưu và nạp game bạn sẽ
gặp phải vấn đề chết người đó là : nó sẽ báo lổi không biết bạn tham chiếu đến
sceneNode nào ? (do lúc đó scenenode đó chưa được nạp).
Cách giải quyết thế nào : thứ nhất bạn phải đặt ID cho SceneNode
được tham chiếu đó để SceneNode của bạn biết cách mà tìm nó. Và cái thứ 2 quan
trọng hơn là bạn hãy thêm vào biến Init như là giá trị khởi tạo. Biến này chỉ
là true khi Node đã được tạo hết (khi đến phần render của Node hay animateNode
của SceneNodeAnimator). Như vậy vấn đề được giải quyết xong.
16) Một số các thủ thuật với GUI :
a)
GUI skin : cần nắm vững nếu
muốn tối ưu GUI
-
Lấy Skin hiện thời :
Guienv->getSkin()
-
Đặt
skin làm skin hiện tại cho GUI : gui->setSkin()
b) Lấy và đổi màu giao diện :
skin->getColor() và skin->setColor() : có thể lấy và đỏi màu hay làm
trong suốt một phần hay toàn bộ các GUI hiện tại.
c) Đổi nội dung văn bản mặc đang hiển thị
trong GUI: skin->setDefaultText()
- như đổi tên chữ Yes hay No hay Ok hay Cancel
trên MsgBox thành Đúng, sai, Đồng ý, Hủy chọn - > vậy là mình có thể việt
hóa hoàn toàn GUI
d) Thay đổi kích thước mắc định của các phần
tử trong GUI : độ lớn của Button, độ lớn của ScrollBar…
e) Còn rất nhiều phần khác trong GUI mà bạn
có thể nghiên cứu trong Skin
17)
Làm
cho một node canh chỉnh theo Normal map của địa hình
-
Lấy Normal map của địa hình
-
Normal.normalize(); //Chuẩn hóa nó
-
core::matrix4
trans = character->getRelativeTransformation();//lấy ma trận chuyển vị của Node mà mình muốn quay
-
vector3df
oldX(1,0,0); //vị trí đi tới mắc định
-
trans.rotateVect(oldX);
//quay vị trí đi tới thành vị trí hiện
thời
-
oldX.normalize(); //chuẩn hóa nó
-
core::vector3df nForward =
oldX.crossProduct(Normal); //Lấy vector
pháp của vector đang đi tới hiện tại với vector Normal
-
if (nForward.getLength()==0)
nForward = vector3df (Normal.Y, Normal.X, Normal.Z) ; //Nếu Normal trùng với hướng di chuyển hiện tại
-
core::vector3df nFRote =
nForward.getHorizontalAngle(); //lấy gốc
quay quanh trục Y của nForward.
-
character->setRotation(nFRote);
//quay Node đi theo vector nFRote vậy là
xong.
18) Quay một Node thành
BillBoard :
- //
make billboard look to camera
core::vector3df pos =
getAbsolutePosition();
core::vector3df campos =
camera->getAbsolutePosition();
core::vector3df target =
camera->getTarget();
core::vector3df up =
camera->getUpVector();
core::vector3df view =
target - campos;
view.normalize();
core::vector3df
horizontal = up.crossProduct(view);
if (
horizontal.getLength() == 0 )
{
horizontal.set(up.Y,up.X,up.Z);
}
horizontal.normalize();
horizontal *= 0.5f *
Size.Width;
core::vector3df vertical
= horizontal.crossProduct(view);
vertical.normalize();
vertical *= 0.5f *
Size.Height;
view *= -1.0f;
for (s32 i=0; i<4 i="i" span="span">4>
vertices[i].Normal = view;
vertices[0].Pos
= pos + horizontal + vertical;
vertices[1].Pos
= pos + horizontal - vertical;
vertices[2].Pos
= pos - horizontal - vertical;
vertices[3].Pos
= pos - horizontal + vertical;
19) Sinh Terrain động từ bộ
nhớ :
heightMap=
myDriver->createImage(video::ECF_A8R8G8B8,dimension2du(257,257));
heightMap->fill(SColor(0,0,0,0));
ge.Generate_LandScape_A(heightMap,52);
ge.FilterToCloud(0,255,3,heightMap);
ge.Emboss(1,5,heightMap);
//dữ liệu trong Memory
c8*
imageData=new c8[heightMap->getImageDataSizeInBytes()];
io::IWriteFile*
memWriteFile
=myDevice->getFileSystem()->createMemoryWriteFile(imageData,heightMap->getImageDataSizeInBytes(),"tempFile.bmp");
//sao chép qua bộ nhớ
myDriver->writeImageToFile(heightMap,memWriteFile);
//lưu lại trên HD để dùng khi nạp lại
myDriver->writeImageToFile(heightMap,"Map.bmp");
//tạo file truy suất đọc trong Memory
io::IReadFile*
rf =
myDevice->getFileSystem()->createMemoryReadFile(imageData,heightMap->getImageDataSizeInBytes(),"Map.bmp");
//Tạo Terrain như bình thường
if (terrain!=0)
terrain->remove();
terrain =
mySmgr->addTerrainSceneNode(rf,0,-1,vector3df(0,0,0),vector3df(0,0,0),vector3df(200,50,200),SColor(255,255,255,255));
20)
CÁC LƯU Ý KHI CHUYỂN SHADER CODE TỪ RENDER
MONKEY SANG IRRLICHT
a)
hệ trục tọa độ khác nhau : trục
Y và Z sẽ chuyển thành Z và Y
b)
Phép nhân ma trận : các ví dụ
mẫu trong RM thì đúng (khi add default) nhưng các ví dụ còn lại sẳn có thì phép
nhân ma trận bị sai :
Out.Pos = mul(view_proj_matrix, Pos);
Sẽ thành :
Out.Pos = mul(Pos , view_proj_matrix); // trong Irrlicht
c)
Ma trận bị Invert : một số ma
trận như ViewMatrix hay ViewInvMatrix khi chuyển qua Irrlicht phải bị Invert
trước một lần nữa.
d)
3D Noise Texture :
- Do Irrlicht không hỗ trợ định dạng này (3D Texture) mà chỉ có 2D
Texture nên phải dùng công thức chuyển đổi (nhưng phải linh hoạt trong nhiều
trường hợp)
- Một lệnh Tex3D trong RM sẽ tương ứng với 2 lệnh Tex2D và một lệnh
lerp trong Irrlicht.
e)
CUBEMAP : Irrlicht cũng không
hỗ trợ định dạng này, nên ta cũng phải thực hiện chuyển đổi (nhất là khi dùng
Reflection từ môi trường) bằng cách dùng Liner View. (Xem ví dụ về Refraction
để biết)
f)
Hằng số : các hằng số khai
trước đều không có tác dụng trong Irrlicht, nên chuyển các hằng số này vào
trong thân hàm.
21) LƯU Ý KHI SCALE NODE LÀ FILE BSP :
Một SceneNode được tạo
nên từ Mesh bsp (như QUAKE) khi dùng lệnh node->setScale() sẽ bị lổi khi
render (bị mất đa giác của meshkhi nhìn ở các hướng khác nhau
Để khắc phục ta không
scale từ sceneNode mà trực tiếp từ mesh bằng hàm scale() trong
IMeshManipulator* ( từ smgr->getMeshManipulator()) như vậy là thành công
không còn lổi render.
22) HIỂN THỊ COMPOUND OBJECT TỪ BULLET SANG IRRLICHT:
Với đối tượng là Compound ta dùng ngay emptySceneNode thay thế nó để hiển thị
Các đối tượng khác là child của nó thì dùng bình thường và nó cũng là child (trong sceneNode của Irrlicht) như vậy thì hiển thị SceneNode dạy diện cho các Object của Bullet dạng Compound mới chính xác (Ví dụ : ForkLiftDemo)
23)
Không có nhận xét nào:
Đăng nhận xét