#include
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#pragma comment(lib, "Irrlicht.lib")
/*
สร้างคลาส MyEventReceiver ใช้สำหรับ receive events ใช้สำหรับรับค่าอินพุตจากเมาส์และคีย์บอร์ด
*/
class MyEventReceiver : public IEventReceiver
{
public:
// This is the one method that we have to implement
virtual bool OnEvent(const SEvent& event)
{
// Remember whether each key is down or up
if (event.EventType == irr::EET_KEY_INPUT_EVENT)
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
// เพิ่มอีเวนท์สำหรับเมาส์
if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
// ถ้าคลิกเมาส์ซ้าย
if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
mouseDown = true;
// ถ้าปล่อยเมาส์ซ้าย
} else if(event.MouseInput.Event == irr::EMIE_LMOUSE_LEFT_UP) {
mouseDown = false;
}
}
return false;
}
// ใช้สำหรับตรวจสอบว่ากดคีย์อะไรไป
virtual bool IsKeyDown(EKEY_CODE keyCode) const
{
return KeyIsDown[keyCode];
}
bool IsLMouseClick() {
return mouseDown;
}
// Constructor กำหนดค่าเริ่มต้นให้การกดคีย์เป็นเท็จ ทุกคีย์
MyEventReceiver()
{
for (u32 i=0; i
KeyIsDown[i] = false;
mouseDown = false;
}
private:
// We use this array to store the current state of each key
bool KeyIsDown[KEY_KEY_CODES_COUNT];
bool mouseDown;
};
int main() {
// สร้าง object ของคลาส MyEventReceiver ใช้สำหรับ recieve events
MyEventReceiver receiver;
IrrlichtDevice *device =
createDevice( EDT_OPENGL, dimension2d(640, 480), 32,
false, false, false, &receiver);
ISceneManager* smgr = device->getSceneManager();
IVideoDriver* driver = device->getVideoDriver();
gui::IGUIFont* font = device->getGUIEnvironment()->getBuiltInFont();
// โหลดฉากสำหรับใช้ทดสอบ
device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
scene::IMeshSceneNode* q3node = 0;
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
/*
So far so good, we've loaded the quake 3 level like in tutorial 2. Now,
here comes something different: We create a triangle selector. A
triangle selector is a class which can fetch the triangles from scene
nodes for doing different things with them, for example collision
detection. There are different triangle selectors, and all can be
created with the ISceneManager. In this example, we create an
OctTreeTriangleSelector, which optimizes the triangle output a little
bit by reducing it like an octree. This is very useful for huge meshes
like quake 3 levels. After we created the triangle selector, we attach
it to the q3node. This is not necessary, but in this way, we do not
need to care for the selector, for example dropping it after we do not
need it anymore.
*/
scene::ITriangleSelector* selector = 0;
if (q3node)
{
q3node->setPosition(core::vector3df(-1350,-130,-1400));
selector = smgr->createOctTreeTriangleSelector(q3node->getMesh(), q3node, 128);
q3node->setTriangleSelector(selector);
// We're not done with this selector yet, so don't drop it.
}
// And this B3D file uses skinned skeletal animation.
scene::IAnimatedMeshSceneNode* node = 0;
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/sydney.md2"));
node->setMaterialFlag(EMF_LIGHTING, false);
node->setMD2Animation(scene::EMAT_STAND);
node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
node->setScale(core::vector3df(2,2,2));
node->setPosition(core::vector3df(-70,-15,-60));
node->setRotation(core::vector3df(0,0,0));
node->setAnimationSpeed(20.f);
node->getMaterial(0).NormalizeNormals = true;
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
selector, node, core::vector3df(10,30,10),
core::vector3df(0,-10,0), core::vector3df(0,15,0));
node->addAnimator(anim);
anim->drop(); // And likewise, drop the animator when we're done referring to it.
selector->drop(); // As soon as we're done with the selector, drop it.
// สร้าง node ของกล้องสำหรับการควบคุมกล้อง
ICameraSceneNode* cam = smgr->addCameraSceneNode();
// ตั้งตำแหน่งกล้อง
cam->setPosition(vector3df(-30,0,0));
// ตั้งแกนให้กล้อง
cam->setRotation(vector3df(0, 0, 0));
// ตั้งในกล้องมองไปที่ตำแหน่งที่ตัวละครอยู่
cam->setTarget(node->getPosition());
// สร้าง billboard เอาไว้ใช้เป็นเป้าหมายในการเดินทางของตัวละคร.
scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
bill->setMaterialFlag(video::EMF_LIGHTING, false);
bill->setMaterialFlag(video::EMF_ZBUFFER, false);
bill->setSize(core::dimension2d(20.0f, 20.0f));
bill->setPosition(core::vector3df(-70,0,-60));
// กำหนด SceneCollisionManager สำหรับเรียกใช้ภายหลัง.
scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
int state = 0;
bool IsMoveChar = false;
while(device->run()) {
// เอาค่า แกน & ตำแหน่งของตัวละครมาใช้สำหรับการตั้งค่าภายหลัง
core::vector3df c = node->getPosition();
core::vector3df c2 = bill->getPosition();
core::vector3df d = node->getRotation();
// หมุนตัวละครหาเป้าหมาย
f32 diff_x = c2.X - c.X;
f32 diff_z = c2.Z - c.Z;
d.Y = ((atan2(diff_x,diff_z)*180)/3.14)-90;
node->setRotation(d);
// หาระยะห่างระหว่างตัวละครกับเป้าหมาย
f32 dis_x = c.X - c2.X;
f32 dis_z = c.Z - c2.Z;
f32 distance = sqrt(dis_x*dis_x + dis_z*dis_z);
// ให้ตัวละครวิ่งไปหาเป้าหมายขณะที่ระยะห่างระว่างตัวละครกับเป้าหมาย >= 30
if (distance >= 30 && IsMoveChar) {
c.X += 0.5 * cos((d.Y) * 3.14 / 180);
c.Z -= 0.5 * sin((d.Y) * 3.14 / 180);
if (state == 0) {
node->setMD2Animation(scene::EMAT_RUN);
state = 1;
}
// ย้ายตำแหน่งวัตถุ
node->setPosition(c);
} else {
if (state == 1) {
node->setMD2Animation(scene::EMAT_STAND);
state = 0;
}
IsMoveChar = false;
}
// หาตำแหน่งของจุดหมุนแกน Y ของตัวละครแล้วเพิ่มไป 90 แล้วคูณด้วย Pi/180
// เพื่อใช้ตั้งตำแหน่งกล้องให้มองติดตามหลังตัวละคร
float diry = ((d.Y+90)*3.14)/180;
// ตั้งตำแหน่งสำหรับใช้กับกล้อง
// โดยเพิ่มระยะในแกน X และ Z คูณ 125 จากวัตถุ และแกน Y อยู่ที่ 100
int xf = (c.X-sin(diry)*125);
int yf = c.Y+50;
int zf =(c.Z-cos(diry)*125);
// ย้ายตำแหน่งกล้อง
cam->setPosition(vector3df(xf,yf,zf));
// ตั้งให้กล้องมองไปที่ตัวละคร
cam->setTarget(node->getPosition());
// Map Picking
// All intersections in this example are done with a ray cast out from the camera to
// a distance of 1000. You can easily modify this to check (e.g.) a bullet
// trajectory or a sword's position, or create a ray from a mouse click position using
// ISceneCollisionManager::getRayFromScreenCoordinates()
core::line3d ray;
// ให้ ray เริ่มต้นที่ตำแหน่งกล้อง
ray.start = cam->getPosition();
// ให้ ray จบ จากโค้ดสรุปได้ว่าให้ความยาว ray = ตำแหน่งจากกล้องไปที่ตำแหน่งที่กล้องหันอยู่
ray.end = ray.start + (cam->getTarget() - ray.start).normalize() * 1000.0f;
// เอาตำแหน่งเมาส์(X,Y)
core::position2d m_position = device->getCursorControl()->getPosition();
// ให้ ray เท่ากับค่า ray จากตำแหน่งเมาส์บนหน้าจอ
ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(m_position,cam);
// ใช้สำหรับบันทึกจุดที่ชนกันระหว่างเมาส์กับ map
core::vector3df intersection;
// ใช้สำหรับแสดงสามเหลี่ยมที่แสดงการชน(ระหว่างเมาส์กับ map)
core::triangle3df hitTriangle;
// This call is all you need to perform ray/triangle collision on every scene node
// that has a triangle selector, including the Quake level mesh. It finds the nearest
// collision point/triangle, and returns the scene node containing that point.
// Irrlicht provides other types of selection, including ray/triangle selector,
// ray/box and ellipse/triangle selector, plus associated helpers.
// See the methods of ISceneCollisionManager
scene::ISceneNode * selectedSceneNode =
collMan->getSceneNodeAndCollisionPointFromRay(
ray,
intersection, // อันนี้เป็นตำแหน่งที่เมาส์ชี้กับ map ของเรา
hitTriangle); // อันนี้เป็นสามเหลี่ยมที่แสดงการชน(ระหว่างเมาส์กับ map)
// ถ้าคลิกเมาส์ให้ตัวละครเริ่มเดินทาง
if (receiver.IsLMouseClick() && !IsMoveChar) {
// ให้เป้าหมายเป็นตำแหน่งของ
bill->setPosition(intersection);
IsMoveChar = true;
}
driver->beginScene(true, true, SColor(255,200,200,200));
smgr->drawAll();
if (font) {
stringw str = ((stringw)"Camera X: " + (stringw)cam->getPosition().X + (stringw)" & Camera Y: " + (stringw)cam->getPosition().Y + (stringw)" & Camera Z: " + (stringw)cam->getPosition().Z);
font->draw(str, core::rect(130,30,300,70), video::SColor(255,255,255,255));
}
driver->endScene();
}
device->drop();
return 0;
}
จากตัวอย่างนี้ได้เพิ่ม Event ของเมาส์เข้าไปเพิ่ม ใช้สำหรับการควบคุมตัวละครให้ไปยังตำแหน่งที่ต้องการ(เหมือนพวกเกม RPG, RTS)
แล้วก็มีการใช้ ray ในการตรวจสอบการชน
เนื่องจากตัวอย่างนี้กล้องยังล็อกกับตัวละครเวลาตัวละครจะหันแล้ววิ่งไปหาเป้าหมายจึงทำให้กล้องหันตามแล้วหันเร็วมากไม่ Smooth เลย จึงทำลายตาได้ ลองปรับแต่งดูนะครับ