Bonjour !
Je voudrais savoir s'il vous plait comment je peux créer "massivement" des ennemis et gérer leur IA globale, sans traiter au cas par cas (évidemment!) ?
Je m'explique : je ne veux pas de code d'IA ni de code de monstres, juste un code qui me permet de créer plusieurs nodes et de leur faire executer des fonctions à tous...
Par exemple je veux pouvoir créer quelques golems, et je voudrais que dès que je suis à proximité de l'un d'eux, ce dernier se dirige vers moi et m'attaque.
Problème, je ne sais ni comment créer plusieurs nodes comme dans un tableau, de leur affecter un mesh, ni comment gérer leur IA de façon globale...
Question qui peut vous sembler être comme "comment on fait un mmorpg trop bien, pliz ?" mais détrompez vous ma question est juste pour savoir, comment ca fonctionne au niveau classe et code...
Merci d'avance !
Voilà mon code en ce moment ! =>
/* This tutorial will show how to implement a camera and character display which is a copy of the one used in World of Warcraft. Its purpose is to offer the Community an example for a camera and movement system for rpg or as a base to develop theyr own movement/camera system. It is based on the irrlicht Tutorial 12 and NightBirds NWN Camera example http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=3245 Atm its all a bitt messy and very unclean but its still work in progress , althought comments or suggestions are always welcome. You can contact me in the Irrlicht Forums Username: Jonchaos or per Email Antharon@Hotmail.com */ #include <irrlicht.h> #include <iostream> using namespace irr; #pragma comment(lib, "Irrlicht.lib") #define CAMERASPEED 0.1f #define PI 3.14159265f #define WIDTH 1024 class Golem { public: Golem(){ this->gIsRunning = false; this->gIsAttacking = 0; this->gIsAttacked = 0; life = 100; } void Pain() { if (this->gIsAttacked == 1) { this->gIsAttacked++; } } private: bool gIsRunning; int gIsAttacking; int gIsAttacked; int life; }; class MyEventReceiver : public IEventReceiver { public: MyEventReceiver(scene::ISceneNode* terrain , scene::IAnimatedMeshSceneNode* ptrCharacter, IrrlichtDevice* ptrDevice, scene::ICameraSceneNode* ptrCamera) { // store pointer to terrain so we can change its drawing mode this->Terrain = terrain; this->Character = ptrCharacter; this->device = ptrDevice; this->pCamera = ptrCamera; this->m_yRotationAngle = 3.14159265*0.5f; this->distance = 130; this->bIsRunning = false; this->bIsAttacking = 0; } void Update() { if (!pCamera || !Character) return; if (m_lastTargetPos != Character->getPosition()) { m_updated = true; m_lastTargetPos = Character->getPosition(); } if (m_updated) { m_updated = false; irr::core::vector3df m_Camerapos = m_lastTargetPos; m_Camerapos.X += distance*sin(m_yRotationAngle); m_Camerapos.Z += distance*cos(m_yRotationAngle); m_Camerapos.Y += distance; pCamera->setPosition(m_Camerapos); pCamera->setTarget(m_lastTargetPos); } } void attack() { if (this->bIsAttacking != 0){ this->bIsAttacking ++; } if (this->bIsAttacking >= 105) { this->bIsAttacking = 0; if(!this->bIsRunning){ Character->setMD2Animation(scene::EMAT_STAND); }else{ Character->setMD2Animation(scene::EMAT_RUN); } } if (this->bIsAttacking == 1) { } } void Zoom(float amount) { distance *= amount; if (distance == 0) distance = 2; //there should be more elaborate capping of things here.. m_updated = true; } bool OnEvent(SEvent event) { Update(); if (event.EventType == EET_MOUSE_INPUT_EVENT) { switch (event.MouseInput.Event) { case EMIE_MOUSE_WHEEL: Zoom(((-event.MouseInput.Wheel)+1)/2+.5f); //make it be from .5 to 1.5 return true; break; } ; } if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) { switch (event.KeyInput.Key) { case irr::KEY_ESCAPE: this->device->closeDevice(); return true; } } if (event.EventType == irr::EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown ) { switch (event.KeyInput.Key) { case irr::KEY_UP: Charpos = Character->getPosition(); Charpos.X = Charpos.X - 10.0f ; Character->setPosition(Charpos); Character->setRotation(core::vector3df(0,180,0)); if (!this->bIsRunning) { Character->setMD2Animation(scene::EMAT_RUN); this->bIsRunning = true; } return true; case irr::KEY_DOWN: Charpos = Character->getPosition(); Charpos.X = Charpos.X + 10.0f ; Character->setPosition(Charpos); Character->setRotation(core::vector3df(0,0,0)); if (!this->bIsRunning) { Character->setMD2Animation(scene::EMAT_RUN); this->bIsRunning = true; } return true; case irr::KEY_LEFT: Charpos = Character->getPosition(); Charpos.Z = Charpos.Z - 10.0f ; Character->setPosition(Charpos); Character->setRotation(core::vector3df(0,90,0)); if (!this->bIsRunning) { Character->setMD2Animation(scene::EMAT_RUN); this->bIsRunning = true; } return true; case irr::KEY_RIGHT: Charpos = Character->getPosition(); Charpos.Z = Charpos.Z + 10.0f ; Character->setPosition(Charpos); Character->setRotation(core::vector3df(0,-90,0)); if (!this->bIsRunning) { Character->setMD2Animation(scene::EMAT_RUN); this->bIsRunning = true; } return true; case irr::KEY_SPACE: if (this->bIsAttacking == 0) { Character->setMD2Animation(scene::EMAT_ATTACK); this->bIsAttacking = 1; } return true; } } // if one of the walk keys is lifted -> stop run animation if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) { switch (event.KeyInput.Key) { case irr::KEY_UP: this->bIsRunning = false; Character->setMD2Animation(scene::EMAT_STAND); return true; case irr::KEY_DOWN: this->bIsRunning = false; Character->setMD2Animation(scene::EMAT_STAND); return true; case irr::KEY_LEFT: this->bIsRunning = false; Character->setMD2Animation(scene::EMAT_STAND); return true; case irr::KEY_RIGHT: this->bIsRunning = false; Character->setMD2Animation(scene::EMAT_STAND); return true; case irr::KEY_SPACE: return true; } } return false; } private: bool bIsRunning; int bIsAttacking; scene::ISceneNode* Terrain; scene::IAnimatedMeshSceneNode* Character; IrrlichtDevice* device; scene::ICameraSceneNode* pCamera; irr::core::vector3df m_lastTargetPos; irr::core::vector3df Charpos; bool m_updated; int MouseX; float distance; float m_yRotationAngle; //the angle about the Y axis }; /* The start of the main function starts like in most other example. We ask the user for the desired renderer and start it up. */ int main() { // let user select driver type video::E_DRIVER_TYPE driverType = video::EDT_OPENGL; // create device IrrlichtDevice* device = createDevice(driverType, core::dimension2d<s32>(1040, 780)); if (device == 0) return 1; // could not create selected driver. /* First, we add standard stuff to the scene: A nice irrlicht engine logo, a small help text, a user controlled camera, and we disable the mouse cursor. */ video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); gui::IGUIEnvironment* env = device->getGUIEnvironment(); driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true); //set other font env->getSkin()->setFont(env->getFont("../../media/fontlucida.png")); // add some help text gui::IGUIStaticText* text = env->addStaticText( L"Vie : 100/100", core::rect<s32>(10,740,250,775), true, true, 0, -1, true); // add camera scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(); camera->setPosition(core::vector3df(1900*2,255*2,3700*2)); camera->setTarget(core::vector3df(2397*2,343*2,2700*2)); camera->setFarValue(12000.0f); //mouse cursor device->getCursorControl()->setVisible(false); smgr->setAmbientLight ( video::SColorf ( 0x00c0c0c0 ) ); // add terrain scene node scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode( "../../media/terrain-heightmap2.bmp", 0, // parent node -1, // node id core::vector3df(0.f, 0.f, 0.f), // position core::vector3df(0.f, 0.f, 0.f), // rotation core::vector3df(40.f, 4.4f, 40.f), // scale video::SColor ( 255, 255, 255, 255 ), // vertexColor, 5, // maxLOD scene::ETPS_17, // patchSize 4 // smoothFactor ); terrain->setMaterialFlag(video::EMF_LIGHTING, true); terrain->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp")); terrain->setMaterialTexture(1, driver->getTexture("../../media/Floor09.jpg")); terrain->setMaterialType(video::EMT_DETAIL_MAP); terrain->scaleTexture(1.0f, 40.0f); //terrain->setDebugDataVisible ( true ); /* To be able to do collision with the terrain, we create a triangle selector. If you want to know what triangle selectors do, just take a look into the collision tutorial. The terrain triangle selector works together with the terrain. To demonstrate this, we create a collision response animator and attach it to the camera, so that the camera will not be able to fly through the terrain. */ // create triangle selector for the terrain scene::ITriangleSelector* selector = smgr->createTerrainTriangleSelector(terrain, 0); terrain->setTriangleSelector(selector); selector->drop(); // create event receiver // create skybox driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true); smgr->addSkyBoxSceneNode( driver->getTexture("../../media/irrlicht2_up.jpg"), driver->getTexture("../../media/irrlicht2_dn.jpg"), driver->getTexture("../../media/irrlicht2_lf.jpg"), driver->getTexture("../../media/irrlicht2_rt.jpg"), driver->getTexture("../../media/irrlicht2_ft.jpg"), driver->getTexture("../../media/irrlicht2_bk.jpg")); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true); smgr->setShadowColor(video::SColor(150,0,255,0)); scene::IAnimatedMeshSceneNode* Character = smgr->addAnimatedMeshSceneNode(smgr->getMesh("knight.md2")); Character->setMaterialFlag(video::EMF_LIGHTING, true); Character->setFrameLoop(160, 183); Character->setAnimationSpeed(40); Character->setMD2Animation(scene::EMAT_STAND); Character->setRotation(core::vector3df(0,180.0f,0)); Character->setMaterialTexture(0, driver->getTexture("knight.bmp")); Character->addShadowVolumeSceneNode(); Character->setPosition(core::vector3df(1900*2,255*2,3700*2)); scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator( selector, Character, core::vector3df(60,10,60), core::vector3df(0,-100,0), core::vector3df(0,50,0)); Character->addAnimator(anim); anim->drop(); //Iron Golem scene::IAnimatedMesh* mesh = smgr->getMesh("Iron_Golem_X.x"); scene::IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh); node->setMaterialFlag(video::EMF_LIGHTING, true); node->setMaterialTexture(0,driver->getTexture("c_golem_iron.tga")); node->setPosition(core::vector3df(1900*2, 255*2, 3650*2)); node->setRotation(core::vector3df(-90,-90,0)); node->setScale(core::vector3df(0.3,0.3,0.3)); node->setFrameLoop(200, 260); node->setAnimationSpeed(20); node->setLoopMode(true); node->addShadowVolumeSceneNode(); scene::ISceneNodeAnimator* anim2 = smgr->createCollisionResponseAnimator( selector, node, core::vector3df(60,10,60), core::vector3df(0,-100,0), core::vector3df(0,10,0)); node->addAnimator(anim2); anim2->drop(); MyEventReceiver receiver(terrain,Character,device,camera); device->setEventReceiver(&receiver); int lastFPS = -1; while(device->run()) if (device->isWindowActive()) { driver->beginScene(true, true, 0 ); receiver.attack(); smgr->drawAll(); env->drawAll(); driver->endScene(); // display frames per second in window title int fps = driver->getFPS(); if (lastFPS != fps) { core::stringw str = L"Messiah for all lights - OpenGL ["; str += driver->getName(); str += "] FPS:"; str += fps; // Also print terrain height of current camera position // We can use camera position because terrain is located at coordinate origin str += " Height: "; str += terrain->getHeight(camera->getAbsolutePosition().X, camera->getAbsolutePosition().Z); device->setWindowCaption(str.c_str()); lastFPS = fps; } } device->drop(); return 0; }
Merci beaucoup !
Hors ligne
shell :
gérer leur IA globale, sans traiter au cas par cas
c'est un peut difficile de ne pas faire du cas par cas pour une ia.
shell :
je voudrais que dès que je suis à proximité de l'un d'eux, ce dernier se dirige vers moi et m'attaque.
ca c'est du cas par cas puisqu'a chaque golem tu dois verifier si la distance est inferieur a une certaine valeur et le faire bouger si c'est le "cas" .
shell :
je ne sais ni comment créer plusieurs nodes comme dans un tableau
bien il y existe plusieur methode le tableau (c++ : [] ) le tableau/vecteur de irrlicht (irr::core::array) ( identique a std::vector<T>), la liste irr::core::list (qui existe en version STL std::list<T>) tu crées une de ces chose ( ex le tableau ac Golem* Golems=new Golem[n] ) est y met des golems ( for( int i...){ Golem[i]=new Golem() ....} ) je ne vois pas ou il peut bien y avoir de probleme .
Je ne sais pas ce que tu entends par "ia global" mais si tu parles d'un "factorisation" du code ( ne pas mettre pour chaque class de monstre des methodes d'un system d'ia) tu crées une class IA qui regroupe toute les fonctions necessaires ( distance , ange de vue , prise de desicion : fuite,appeller de l'aide,defendre,attaquer ...)et tu y fais appel pour chaque monstre.
Hors ligne
Perso je ferais une classe "Monstre" qui comporterait un pointeur vers une instance d'une classe "MonstreType" et un pointeur vers une instance d'une autre classe nommée "IA"
MonstreType serait pour stocker tes types de monstres :
MonstreType* Golem = new MonstreType("Golem"); Monstre monstre1; monstre1.SetType( Golem );
Ca permettrait de ne pas hardcoder les différents monstres (tu ne vas pas faire une classe par monstre, c'est du délire !)
Donc Monstre symboliserait une des bestioles de ton jeu, à laquelle serait associée un type (ici Golem) et un objet de type "IA".
Pour l'IA je ferais plusieurs classes filles d'IA comme : IAPassif , IAAgressif, IADefensif, etc.
Histoire d'avoir un choix de comportement pour les monstres.
La classe Monstre aurait une méthode "Update" qui mettrait à jour le monstre et appellerait la méthode "Update" de l'objet IA associé.
Et c'est cette IA::Update() qui serait chargé de vérifier la distance avec le joueur et de les attaquer. Ou pas, selon l'IA (ex: IADefensif ne ferait que riposter, IAFaible fuirait si le nombre de PV est inférieur à tant, etc)
C'est peut être pas très clair (l'heure n'aide pas ^^) mais l'idée est là, n'hésites pas à demander plus de précisions, là je suis un peu fatigué.
Hors ligne
Sisi, c'était très clair. Je vois comment faire dans la programmation et la mise en ordre du code !
Juste une question, mon code (même si tout n'est pas de moi, bien sûr) est-il en revanche bien organisé ? J'ai quelques soucis de soins là dessus, et si pouviez juste jeter un oeil (pas en profondeur, évidemment) et me dire si c'est correct ou si avec le temps je risque d'être submergé par des lignes parasites...
Merci de votre patience à aider des nouveaux dans irrlicht comme moi
A plus tard
Hors ligne
bien si le code reste petit en taille ( juste un exercice ) tu peux laisser comme ca mais pour un projet ( meme tres modeste ) il faut mieu separer les classes ( chaque classe dans un .h et .cpp ) ainsi tu ne fais pas fondre ta mollette quand tu recherches une ligne particuliere , tu pourra réutiliser le code plus facilement ( puisque regroupé ) et surtout que c'est comme ca que ca se fait .Faire aussi un classe principale ou tu y mets ta boucle principale le main devient simplement :
#include ClassPrincipale.h" #define blablablabla .... int main(){ ClassPrincipale* cp=new cp() ; cp->launchgame() }
Hors ligne