DarkAI test example in C++ with sparky's collision by Anonymous Coder16th Jan 2011 8:16
|
---|
Summary Simple implementation of the darkAI engine to test its functionality. Includes friendly AI that follow player and enemy AI which patrol the stage. AI's will engage when in contact. Description DarkAI test application Code ` This code was downloaded from The Game Creators ` It is reproduced here with full permission ` http://www.thegamecreators.com =================================================================================== Main.cpp =================================================================================== // Dark GDK - The Game Creators - www.thegamecreators.com /** This programs creates 2 teams of enemies and firendlies. When ordered the friendlies will follow the player and engage the enemies on contact. The eneimes partol the level until they "hear" or see the player and/or any of the friendlies. */ // whenever using Dark GDK you must ensure you include the header file #include "DarkGDK.h" #include "Game.h" // the main entry point for the application is this function void DarkGDK ( void ) { // set display mode dbSetDisplayMode ( 800, 600, 32 ); dbSetWindowPosition ( GetSystemMetrics ( SM_CXSCREEN ) / 2 - ( 800 / 2 ), GetSystemMetrics ( SM_CYSCREEN ) / 2 - ( 600 / 2 ) ); // turn on sync rate and set maximum rate to 60 fps dbSyncOn ( ); dbSyncRate ( 60 ); //set up view point dbAutoCamOff(); dbPositionCamera(0, 300, -700); dbXRotateCamera ( 25 ); Game game; game.startGame(); // our main loop while ( LoopGDK ( ) ) { //Create game call update game.updateGame(); dbSync ( ); } // return back to windows return; } =================================================================================== Game.cpp =================================================================================== #include "Game.h" #include "soundManager.h" //This is our main game code //Initialise the test area void Game::startGame(){ ptimer = 0; btimer = 0; ftimer = 0; stimer = 0; numFriends = 3; numEnemies = 3; iObjNum = 1; //Start the darkAI engine AIStart ( ); //Start the collision engine SC_Start(); // this command sets the global radius that will be used by all entities. // tis controls how much space is allowed between the edge of obstacles // and the waypoints that define movement around them, since entities // use the waypoints to navigate around their world they will attempt // to maintain at least radius# distance from all obstacles AISetRadius( 2.5 ); AIDebugShowSounds ( 30 ); makeLevel(); loadSounds(); //load the enemyAI enemyBase temp; temp.createEnemy(iObjNum); // make a patrol path AIMakePath ( 1 ); AIPathAddPoint ( 1, 400, 400 ); AIPathAddPoint ( 1, -400, 400 ); //create the instances of the enemies enemies = new enemyBase[numEnemies]; iObjNum = 5005; for(int i = 0; i < numEnemies; i ++) { enemies[i].cloneEnemy(iObjNum,i * 100, 2.5f, 40, 30); iObjNum++; } //make the player player.createPlayer(1001); //range of friendlies 3000-4000 iObjNum = 3000; friends = new friendlyBase[numFriends]; for(int j = 0; j < numFriends; j ++) { friends[j].clonefriendly(iObjNum, -50+(j*50),0, -450, 30); iObjNum++; } } void Game::makeLevel() { //load the level //Create box centered about the origin dbMakeObjectBox(iObjNum, 1000, 10, 1000); dbPositionObject(iObjNum, 0,-10,0); dbColorObject(iObjNum, dbRGB(30,30,50)); SC_SetupObject( iObjNum,1,2 ); iObjNum++; //load the barriers dbMakeObjectBox(iObjNum, 400, 80, 50); dbPositionObject(iObjNum,300,0,-300); dbColorObject(iObjNum, dbRGB(255,0,50)); SC_SetupObject( iObjNum,1,2 ); AIAddStaticObstacle ( iObjNum ); iObjNum++; //load the barriers dbMakeObjectBox(iObjNum, 400, 80, 50); dbPositionObject(iObjNum,-300,0,-300); dbColorObject(iObjNum, dbRGB(255,0,50)); SC_SetupObject( iObjNum,1,2 ); AIAddStaticObstacle ( iObjNum ); iObjNum++; AIStartNewObstacle ( ); // add points in a anti-clockwise direction to // create a boundary (use clockwise to create an obstacle) AIAddObstacleVertex ( -500, -500 ); AIAddObstacleVertex ( 500, -500 ); AIAddObstacleVertex ( 500, 500 ); AIAddObstacleVertex ( -500, 500 ); // finish creating our boundary AIEndNewObstacle ( 0, 1 ); // finalise our obstacles AICompleteObstacles ( ); } void Game::updateGame(){ // hide or show paths if ( dbKeyState ( 2 ) && ptimer < dbTimer ( ) ) { // update timer and mode ptimer = dbTimer ( ) + 300; pMode = 1 - pMode; // decide what to do if ( pMode == 0 ) AIDebugHidePaths ( ); else AIDebugShowPaths ( 2.5 ); } // hide or show obstacle bounds if ( dbKeyState ( 3 ) && btimer < dbTimer ( ) ) { // update timer and mode btimer = dbTimer ( ) + 300; bMode = 1 - bMode; // decide what to do if ( bMode == 0 ) AIDebugHideObstacleBounds ( 0 ); else AIDebugShowObstacleBounds ( 0, 2.5 ); } //update the player player.updatePlayer(); //control friendlies if ( dbKeyState ( 33 )&& stimer < dbTimer ( ) ) { // update timer ftimer = dbTimer ( ) + 300; // follow player AITeamFollowPlayer( 20.0f); } // separate action && stimer < dbTimer ( ) if ( dbKeyState ( 35 )&& stimer < dbTimer ( ) ) { // update timer stimer = dbTimer ( ) + 300; for(int i = 0; i < numFriends; i++) { friends[i].hold(); } } // display information dbSetCursor ( 0,0 ); dbPrint ( "Enemies Are Red, Friendlies Are Green" ); dbPrint ( ); dbPrint ( "Controls: " ); dbPrint ("[Mouse] Camera"); dbPrint ( "[ArrowKeys] Move Camera" ); dbPrint ( "[Left-mouse] Simulates player firing weapons, attracts enemies in range"); dbPrint ( "[WASD] Move Player" ); dbPrint ( "[SPACE] Jump Player" ); dbPrint ( "[F] Make Friendlies Follow Player" ); dbPrint ( "[H] Hold Position" ); dbPrint ("[M] Move to Waypoint Maker "); dbPrint ( ); dbPrint ( "Debug Controls: " ); dbPrint ( "[1] Toggle Entity Path" ); dbPrint ( "[2] Toggle Obstacle Bounds" ); dbPrint ( ); dbPrintC ( "FPS: " ); dbPrint ( ( LONGLONG ) dbScreenFPS ( ) ); //update all AI entities for(int i = 0; i < numEnemies;i++) { enemies[i].updateEnemy(); } for(int i =0; i < numFriends; i++) { friends[i].updateFriendlies(); } //update the darkAI AIUpdate(); } =================================================================================== Game.h =================================================================================== #ifndef Game_H #define Game_H #include "DarkGDK.h" #include "DarkAI.h" #include "friendlyBase.h" #include "SC_Collision.h" #include "player.h" #include "enemyBase.h" #include "soundManager.h" class Game { private: int iObjNum; int bMode, pMode; int ptimer, btimer, ftimer, stimer; player player; enemyBase *enemies; int numEnemies; friendlyBase *friends; int numFriends; public: void startGame(); void makeLevel(); void updateGame(); }; #endif =================================================================================== player.cpp =================================================================================== #include "DarkGDK.h" #include "DarkAI.h" #include "player.h" #include "SC_Collision.h" #include "soundManager.h" void player::createPlayer(int iObjNum) { //player variables used by sparkies collision g_fSpeed = 5.0f; g_fTurn = 0.03f; gravity = -0.1f; slope = 0.2f; ground = 1; jumptimer = 0; //postion vector float vx = 0; float vy = 0; float vz = 0; iPlayerNum = iObjNum; //create player object dbMakeObjectSphere(iPlayerNum, 20); dbPositionObject(iPlayerNum, 0, 0, -400); dbColorObject(iPlayerNum, dbRGB(0,0,235)); AIAddPlayer(iPlayerNum); SC_SetupObject( iPlayerNum,0,1 ); } void player::movePlayer() { dbYRotateObject( iPlayerNum,dbObjectAngleY(iPlayerNum) + dbMouseMoveX()/3.0f ); dbXRotateObject( iPlayerNum,dbObjectAngleX(iPlayerNum) + dbMouseMoveY()/3.0f ); float oldx = dbObjectPositionX(iPlayerNum); float oldy = dbObjectPositionY(iPlayerNum); float oldz = dbObjectPositionZ(iPlayerNum); //apply gravity, and user changes to movement float angy = dbObjectAngleY(iPlayerNum); vx = 0; vz = 0; //if player is jumping or falling then apply 'normal' gravity //if not attempt to keep the player stuck to the floor if ( vy == 0 && jumptimer == 0 ) vy = vy + 10*gravity; else vy = vy + gravity; if (dbKeyState(32) == 1 ) { vx = vx + dbCos(angy); vz = vz - dbSin(angy); } if (dbKeyState(30) == 1 ) { vx = vx - dbCos(angy); vz = vz + dbSin(angy); } if (dbKeyState(31) == 1 ) { vx = vx - dbSin(angy); vz = vz - dbCos(angy); } if (dbKeyState(17) == 1 ) { vx = vx + dbSin(angy); vz = vz + dbCos(angy); } //only jump if on ground, and a certain time after last jump if ( ground == 1 ) { if ( dbSpaceKey() == 1 && jumptimer == 0 ) { vy = vy + 4.0f; jumptimer = 20; } } //can add value here to increase speed //this would be the player's final position without collision float x = oldx + vx; float y = oldy + vy; float z = oldz + vz; //first handle gravity - seperated from horizontal movement //to achieve a more realistic effect //Method: simple - cast a sphere vertically down, if it hits the level then // position the object there (sticky collision) then move // on to horizontal movement // more complex - if we were to only use the simple method the player would be // allowed to climb almost vertical slopes. Alternative is to // get the normalY direction to work out how flat the gorund // below the player is, 0-slope# is flatter, slope#-1 is steeper. // if it's flat, use sticky collision, if it's steep slide the // player down the slope. Changing slope# determines how steep the // player can climb. NOTE: this also effects stairs. int collide = SC_SphereCastGroup( 1, oldx,oldy,oldz, oldx,oldy+vy,oldz, 10,0 ); if ( collide > 0 ) { //how flat is this ground float ny = SC_GetCollisionNormalY(); if ( dbAbs(ny) > slope ) { //FLAT, stick oldy = SC_GetStaticCollisionY(); } else { //STEEP, slide x = x - oldx; z = z - oldz; oldx = SC_GetCollisionSlideX(); oldy = SC_GetCollisionSlideY(); oldz = SC_GetCollisionSlideZ(); x = x + oldx; z = z + oldz; } //ny#<0 means the player has hit a ceiling rather than a floor if ( ny > slope ) { //only on ground if standing on flat ground ground = 1; vy = 0; } else { ground = 0; //if player has hit a flat ceiling then stop vy# movement if ( ny < -slope ) vy = gravity; } } else { //nothing below player, not on ground, add vertical speed to player oldy = oldy + vy; ground = 0; } //jumptimer will decrease only when player is back on ground //creates a pause between two successive jumps if ( ground == 1 && jumptimer > 0 ) jumptimer--; //handle horizontal movement as sliding //player only collides with group 1 (level) objs and moves freely through others collide = SC_SphereSlideGroup( 1, oldx,oldy,oldz, x,oldy,z, 10,0 ); if ( collide > 0 ) { //if hit, reposition player, halt movement vector x = SC_GetCollisionSlideX(); oldy = SC_GetCollisionSlideY(); z = SC_GetCollisionSlideZ(); vx = 0; vz = 0; //possible code for giving the player a jumping help up stairs... //might be useful if slope# is set very high but stairs are still required //dy = oldy - SC_GetStaticCollisionY() //if ( dy < slope && dy > 0 && ground == 1 ) vy = 0.5; } //position the player dbPositionObject( iPlayerNum,x,oldy,z ); //create sound in the AI system at the player co-ords if(dbMouseClick() == 1 ) { AICreateSound ( x, z, 1, 200 ); playSound("shoot"); } SC_UpdateObject( iPlayerNum ); } void player::updatePlayer(){ dbPositionMouse(dbScreenWidth()/2, dbScreenHeight()/2); dbControlCameraUsingArrowKeys ( 0, g_fSpeed, g_fTurn ); g_fOldCamAngleY = g_fCameraAngleY; g_fOldCamAngleX = g_fCameraAngleX; g_fCameraAngleY = dbWrapValue ( g_fCameraAngleY + dbMouseMoveX ( ) * 0.4f ); g_fCameraAngleX = dbWrapValue ( g_fCameraAngleX + dbMouseMoveY ( ) * 0.4f ); dbYRotateCamera ( dbCurveAngle ( g_fCameraAngleY, g_fOldCamAngleY, 24 ) ); dbXRotateCamera ( dbCurveAngle ( g_fCameraAngleX, g_fOldCamAngleX, 24 ) ); //move the player object movePlayer(); } =================================================================================== player.h =================================================================================== #ifndef player_H #define player_H class player { protected: float lasttime; float g_fSpeed, g_fTurn, g_fOldCamAngleX,g_fOldCamAngleY, g_fCameraAngleX,g_fCameraAngleY; int iPlayerNum; float gravity, slope; int ground, jumptimer; float vx,vy,vz; int health; public: void createPlayer(int iObjNum); void movePlayer(); void updatePlayer(); }; #endif =================================================================================== enemyBase.cpp =================================================================================== #include "DarkGDK.h" #include "DarkAI.h" #include "enemyBase.h" #include "soundManager.h" enemyBase::enemyBase() { iAssociatedObject = 0; shootTimer = 0; } //move object construction code to object manager void enemyBase::createEnemy(int iObjNum) { //build an enemy dbMakeObjectCone ( iObjNum, 20 ); dbXRotateObject ( iObjNum, 90 ); dbFixObjectPivot ( iObjNum ); dbMakeMeshFromObject (5001, iObjNum); dbDeleteObject ( iObjNum ); // attack template MakePlane ( iObjNum+1500, 0.3f, 0.3f, 1 ); dbXRotateObject ( iObjNum+1500,90 ); dbFixObjectPivot ( iObjNum+1500 ); dbMakeMeshFromObject ( 5002, iObjNum+1500 ); dbDeleteObject ( iObjNum+1500 ); } //Create this enemy as a clone of an existing object //creates a specific object to be associated with each instance of this class void enemyBase::cloneEnemy(int iObjNum, int startPosX, int startPosY, int startPosZ, int speed) { iAssociatedObject = iObjNum; health = 100; dbMakeObject ( iObjNum, 5001, 0 ); dbPositionObject ( iObjNum, startPosX, startPosY, startPosZ ); dbColorObject ( iObjNum, dbRgb ( 255, 0, 0 ) ); //make fire solutiom dbMakeObject ( iObjNum + 1000, 5002, 1 ); dbSetObjectLight ( iObjNum + 1000, 0 ); dbHideObject ( iObjNum + 1000 ); // add them to the enemy team AIAddEnemy ( iObjNum ); AISetEntitySpeed ( iObjNum, speed ); AISetEntityAggressive ( iObjNum ); AISetEntityViewArc ( iObjNum, 90, 180 ); AISetEntityViewRange ( iObjNum, 200 ); AISetEntityHearingRange ( iObjNum, 300 ); AIEntityAssignPatrolPath ( iObjNum, 1 ); } //Delete the objects associate with this instance of enemy void enemyBase::DestroyEnemy() { AIKillEntity(iAssociatedObject); dbDeleteObject(iAssociatedObject); dbDeleteObject(iAssociatedObject+1000 ); } //update the current enemy void enemyBase::updateEnemy(){ if(dbObjectExist(iAssociatedObject)) { // hide attack lines if ( dbObjectExist ( iAssociatedObject+1000 ) && shootTimer < 120 ) { dbPositionObject(iAssociatedObject+1000, dbObjectPositionX(iAssociatedObject+1000), 30.0, dbObjectPositionZ(iAssociatedObject+1000)); dbHideObject( iAssociatedObject+1000 ); } //fire control if ( AIEntityExist ( iAssociatedObject ) == 1 ) { // check firing state if ( AIGetEntityCanFire ( iAssociatedObject ) && shootTimer <= 0 ) { // determine new position float tx = AIGetEntityTargetX ( iAssociatedObject ); float tz = AIGetEntityTargetZ ( iAssociatedObject ); float x = dbObjectPositionX ( iAssociatedObject ); float z = dbObjectPositionZ ( iAssociatedObject ); float dx = ( x + tx ) / 2.0f; float dz = ( z + tz ) / 2.0f; float dist = sqrt ( ( tx - x ) * ( tx - x ) + ( tz - z ) * ( tz - z ) ); float ang = acos ( ( tz - z ) / dist ) * 57.29577951308f; if ( ( tx - x ) < 0 ) ang = 360 - ang; dbPositionObject( iAssociatedObject + 1000, dx, 0.0f, dz ); dbYRotateObject ( iAssociatedObject + 1000, ang + 0.1f ); dbScaleObject ( iAssociatedObject + 1000, 100, 100, 300 * dist ); dbShowObject ( iAssociatedObject + 1000 ); shootTimer = 150; AICreateSound ( x, z, 1, 100 ); playSound("shoot"); } } // update timer if ( shootTimer > 0 ) shootTimer -= 2; //has the eenmy been shot int collision = dbObjectCollision(iAssociatedObject,0); //has a friendly bullet hit the enemy if(collision >= 4000 && collision < 4020) { health-=0.1; } if(health == 0) { DestroyEnemy(); } } } void enemyBase::MakePlane(int iID, float fWidth, float fHeight, int iFlag) { dbMakeObjectBox(iID, fWidth, fHeight, iFlag); } =================================================================================== enemyBase.h =================================================================================== #ifndef enemyBase_H #define enemyBase_H class enemyBase { protected: int iAssociatedObject; int shootTimer; int health; public: enemyBase(); virtual void createEnemy(int iObjNum); virtual void cloneEnemy(int iObjNum, int startPosX, int startPosY, int startPosZ, int speed); virtual void DestroyEnemy(); virtual void updateEnemy(); void MakePlane ( int iID, float fWidth, float fHeight, int iFlag ); }; #endif =================================================================================== friendlyBase.cpp =================================================================================== #include "DarkGDK.h" #include "DarkAI.h" #include "soundManager.h" #include "friendlyBase.h" friendlyBase::friendlyBase() { iAssociatedObject = 0; } void friendlyBase::clonefriendly(int iObjNum, int startPosX, int startPosY, int startPosZ, int speed) { iAssociatedObject = iObjNum; int health = 100; //create firendly from standard template, replace code when model importer added dbMakeObject ( iObjNum, 5001, 0 ); dbPositionObject ( iObjNum, startPosX, startPosY, startPosZ ); dbColorObject ( iObjNum, dbRgb ( 0, 255, 0 ) ); dbMakeObject ( iObjNum + 1000, 5002, 2 ); dbPositionObject ( iObjNum+1000, startPosX, startPosY, startPosZ ); dbSetObjectLight ( iObjNum + 1000, 0 ); dbHideObject ( iObjNum + 1000 ); // add them to the friendly team AIAddFriendly ( iObjNum ); AISetEntitySpeed ( iObjNum, speed ); AISetEntityViewArc ( iObjNum, 90, 180 ); AISetEntityViewRange ( iObjNum, 200 ); AISetEntityHearingRange ( iObjNum, 250 ); } void friendlyBase::DestroyFriendly() { AIKillEntity(iAssociatedObject); dbDeleteObject(iAssociatedObject); dbDeleteObject(iAssociatedObject+1000 ); } void friendlyBase::updateFriendlies() { if(dbObjectExist(iAssociatedObject)) { // hide attack lines if ( dbObjectExist ( iAssociatedObject+1000 ) && shootTimer < 120 ) { dbPositionObject(iAssociatedObject+1000, dbObjectPositionX(iAssociatedObject+1000), 30.0, dbObjectPositionZ(iAssociatedObject+1000)); dbHideObject( iAssociatedObject+1000 ); } //fire control if ( AIEntityExist ( iAssociatedObject ) == 1 ) { // check firing state if ( AIGetEntityCanFire ( iAssociatedObject ) && shootTimer <= 0 ) { // determine new position float tx = AIGetEntityTargetX ( iAssociatedObject ); float tz = AIGetEntityTargetZ ( iAssociatedObject ); float x = dbObjectPositionX ( iAssociatedObject ); float z = dbObjectPositionZ ( iAssociatedObject ); float dx = ( x + tx ) / 2.0f; float dz = ( z + tz ) / 2.0f; float dist = sqrt ( ( tx - x ) * ( tx - x ) + ( tz - z ) * ( tz - z ) ); float ang = acos ( ( tz - z ) / dist ) * 57.29577951308f; if ( ( tx - x ) < 0 ) ang = 360 - ang; dbPositionObject( iAssociatedObject + 1000, dx, 0.0f, dz ); dbYRotateObject ( iAssociatedObject + 1000, ang + 0.1f ); dbScaleObject ( iAssociatedObject + 1000, 100, 100, 300 * dist ); dbShowObject ( iAssociatedObject + 1000 ); shootTimer = 150; AICreateSound ( x, z, 1, 100 ); playSound("shoot2"); } } // update timer if ( shootTimer > 0 ) shootTimer -= 2; //has the eenmy been shot int collision = dbObjectCollision(iAssociatedObject,0); //has a friendly bullet hit the enemy if(collision >= 4000 && collision < 4020) { health-=0.1; } if(health == 0) { DestroyFriendly(); } } } void friendlyBase::hold() { AIEntitySeparate(iAssociatedObject); AIEntityHoldPosition(iAssociatedObject); } =================================================================================== friendlyBase.h =================================================================================== #ifndef friendlyBase_H #define friendlyBase_H class friendlyBase { protected: int iAssociatedObject; int shootTimer; int health; public: friendlyBase(); void DestroyFriendly(); virtual void clonefriendly(int iObjNum, int startPosX, int startPosY, int startPosZ, int speed); void updateFriendlies(); void MakePlane ( int iID, float fWidth, float fHeight, int iFlag ); void hold(); }; #endif |