flysouth Posted July 12, 2016 Share Posted July 12, 2016 Looking at the navmesh documentation seems to indicate that the methods have been implemented in the AiPlayer class. What happens if I want an Ai controlled hover vehicle that can follow nav paths for example? All sane suggestions will be apreciated. :mrgreen: Quote Link to comment Share on other sites More sharing options...
flysouth Posted July 13, 2016 Author Share Posted July 13, 2016 anybody know? Quote Link to comment Share on other sites More sharing options...
Steve_Yorkshire Posted July 14, 2016 Share Posted July 14, 2016 AIPlayer uses the navmesh so you'd mount them on a vehicle as standard. AI should attempt to follow the route, though they won't understand steering arcs so are likely to miss waypoints. Quote Link to comment Share on other sites More sharing options...
flysouth Posted July 14, 2016 Author Share Posted July 14, 2016 Thanks for the suggestion. Quote Link to comment Share on other sites More sharing options...
Jason Campbell Posted July 14, 2016 Share Posted July 14, 2016 There was an AIWheeledVehicle resource over at GG but the code link is dead. You would have to re-write the AI code because as soon as stock AI enters a vehicle they drive around like a madman. Quote Link to comment Share on other sites More sharing options...
Azaezel Posted July 14, 2016 Share Posted July 14, 2016 (edited) This will need conversion work but here's a direct extract from one of our old games for folks to play with based on the resource Jason mentioned:PT1 - strategic AI (shared across the racers so they all get more agressive the better folks do. 10-1 skippable, but there's references) #ifndef _AIStrategy_H_ #define _AIStrategy_H_ #ifndef _GAMEBASE_H_ #include "console/simBase.h" #endif #include "console/console.h" #include "console/consoleTypes.h" #include "core/bitStream.h" class SAI : public SimObject { typedef SimObject Parent; public: F32 mAgression; F32 mPrecision; SAI(); ~SAI(); bool onAdd(); void onRemove(); DECLARE_CONOBJECT(SAI); static void initPersistFields(); }; DECLARE_CONSOLETYPE(SAI) #endif #include "AIStrategy.h" IMPLEMENT_CONOBJECT(SAI); SAI::SAI() { mAgression = 0.1; mPrecision = 0.1; } SAI::~SAI() { } void SAI::initPersistFields() { addField("Agression", TypeF32, Offset(mAgression, SAI)); addField("Precision", TypeF32, Offset(mPrecision, SAI)); } bool SAI::onAdd() { if(!Parent::onAdd()) return false; return true; } void SAI::onRemove() { Parent::onRemove(); } Pt 2- our lil bot-heads // AIWheeledVehicleBrain.h // Defines a wheeled vehicle that is driven by AI #ifndef _AIWheeledVehicleBrain_h #define _AIWheeledVehicleBrain_h #ifndef _AIStrategy_H_ #include "game/AI/AIStrategy.h" #endif #ifndef _WHEELEDVEHICLE_H_ #include "game/vehicles/wheeledVehicle.h" #endif #include "stdio.h" class PathPoint { public: Point3F m_Point; float m_desiredSpeed; }; class AIWheeledVehicleBrain : public WheeledVehicle { typedef WheeledVehicle Parent; public: enum MoveState { ModeStop, ModeMove, ModeStuck, ModeReverse, }; enum DrivingState { SteerNull, Left, Right, Straight, TurnAround }; enum FeelerState { FrontFeeler, LeftFeeler, RightFeeler, Open, }; protected: //vehicle info Point3F mVehiclePos; F32 mCurrentSpeed; MoveState mMoveState; F32 mMoveSpeed; F32 mMoveTolerance; // Distance from destination before we stop Point3F mMoveDestination; // Destination for movement Point3F mLastLocation; // For stuck check bool mbBetweenLines; F32 mTurnAngle; S32 mCurrentNode; S32 mLastNode; bool mbInit; int mTargetNode; F32 mMovecount; Point3F mstartPos; F32 mDistance; Point3F mIntersection; VectorF mRacelinedir; VectorF mVehicledir; Vector<PathPoint> mPath; Vector<F32> mvecStop; F32 followRacingLine(); int CalcPrevNode(int node); int CalcNextNode(int node); F32 mStuckTolerance; //minimal motion before were considered stuck S32 mMaxStuckCount; //max cycles before stuck turns into totally stuck S32 mStuckCycles; //cycles we've been stuck S32 mStuckCount; //times we've exceeded max stuck cycles allowable F32 mLostDistance; //maximum distance before were considered lost F32 mPrecision; bool mstart; void SetLastNode(); // Utility Methods void throwCallback( const char *name ); virtual bool getAIMove(Move* move); public: AIWheeledVehicleBrain(); void setMoveTolerance( const F32 tolerance ); F32 getMoveTolerance() const { return mMoveTolerance; } void setMoveDestination( const Point3F &location ); Point3F getMoveDestination() const { return mMoveDestination; } F32 avoidCollisions(void); void addPathPoint( const Point3F &location, const float &speed); void setStartNode(int startnode); void BuildStopVector(); void InitAI(); static void initPersistFields(); F32 getDistanceToNode(U32 node); void updateVehicleData(); F32 getMaxTurnAngle(F32 currentSpeed); F32 getSlowDownDistance(F32 currentspeed,F32 desiredspeed); F32 TurnToRaceLine(Point3F vehicledir,Point3F racelinedir,F32 maxTurnAngle); // Steering DrivingState steerState; F32 mLastSteered; F32 getSteeringAngle(); virtual bool onNewSAI(SAI* dptr); SAI* mSAI; bool setSAI(SAI* dptr); SAI* getSAI() { return mSAI; } F32 mAvoidanceTick; bool mAvoiding; DECLARE_CONOBJECT(AIWheeledVehicleBrain); }; bool DistancePointToLine( Point3F Point, Point3F LineStart, Point3F LineEnd, F32 &distance, Point3F &Intersection ); #endif #include "AIWheeledVehicleBrain.h" #include "math/mMatrix.h" #include "math/mPoint.h" #include "core/realComp.h" //TWEAKABLE constants #define STOPSAFETY 15 //How we pad the stop distance value to make sure car can stop in time #define TURNANGLE 0.5 #define LOSTDISTANCE 30 //When vehicle is this far treat as lost #define MAXLOSTSPEED 20 //Max speed when lost IMPLEMENT_CO_NETOBJECT_V1(AIWheeledVehicleBrain); AIWheeledVehicleBrain::AIWheeledVehicleBrain() : WheeledVehicle() { mMoveDestination.set( 0.0f, 0.0f, 0.0f ); mMoveSpeed = 1.0f; mMoveTolerance = 0.25f; mbInit = false; mDistance = 1e20f; mMovecount = 0; mCurrentNode = 0; mStuckCount = 0; mMaxStuckCount = 50; mStuckCycles = 0; mLostDistance = LOSTDISTANCE; mPrecision = 20; mStuckTolerance = 0.01f; mstart = false; mAvoidanceTick = 2; mAvoiding = false; } void AIWheeledVehicleBrain::initPersistFields() { Parent::initPersistFields(); addField("StuckTolerance",TypeF32,Offset(mStuckTolerance,AIWheeledVehicleBrain)); addField("MaxStuckCount",TypeS32,Offset(mMaxStuckCount,AIWheeledVehicleBrain)); addField("LostDistance",TypeF32,Offset(mLostDistance,AIWheeledVehicleBrain)); addField("Precision",TypeF32,Offset(mPrecision,AIWheeledVehicleBrain)); addField("AvoidanceTick",TypeF32,Offset(mAvoidanceTick,AIWheeledVehicleBrain)); } /** * Sets how far away from the move location is considered * "on target" * * @param tolerance Movement tolerance for error */ void AIWheeledVehicleBrain::setMoveTolerance( const F32 tolerance ) { mMoveTolerance = getMax( 0.1f, tolerance ); } void AIWheeledVehicleBrain::addPathPoint( const Point3F &location, const float &speed) { PathPoint v; v.m_Point = location; v.m_desiredSpeed = speed; mPath.push_back(v); } void AIWheeledVehicleBrain::InitAI() { BuildStopVector(); mbInit = true; return; } /* Adjust this function for the performance of your vehicle mvecStop is a vector that store the stop distance need by the vehicle at a certain speed */ void AIWheeledVehicleBrain::BuildStopVector() { int maxSpeed = 250; F32 factor = 0.05f; F32 value = 0; for (int i=0;i<maxSpeed;i++) { value = (i*i)*factor; mvecStop.push_back(value); } } /** * Sets the location for the bot to run to * * @param location Point to run to */ void AIWheeledVehicleBrain::setMoveDestination( const Point3F &location) { mMoveDestination = location; mMoveState = ModeMove; } //Try to have vehicle follow racing line / Path F32 AIWheeledVehicleBrain::followRacingLine() { Point3F v1 = mIntersection-mVehiclePos; v1.z = 0; Point3F v2 = mIntersection-mPath[mCurrentNode].m_Point; v2.z = 0; v1.normalize(); v2.normalize(); Point3F lv = mCross(v1,v2); //Use cross product to figure out if we are left or right of racing line if (lv.z<0) { //vehicle is left of racing line return TurnToRaceLine(mVehicledir,mRacelinedir,TURNANGLE); } else { //vehicle is right of racing line return TurnToRaceLine(mVehicledir,mRacelinedir,-TURNANGLE); } return 0; } F32 AIWheeledVehicleBrain::TurnToRaceLine(Point3F vehicledir,Point3F racelinedir,F32 maxTurnAngle) { Point3F desiredDir; F32 ratio = mDistance/10; if (ratio>1) ratio = 1; else if (ratio<(1/10)) ratio = 0; //Square this value to have a smaller turn angle as we get closer to the racing line ratio =ratio *ratio; F32 turnAngle = ratio*maxTurnAngle; F32 targetDistance = getDistanceToNode(mTargetNode); if (targetDistance<mPrecision) { //Just aim right for the node instead of following the racing line desiredDir = mPath[mTargetNode].m_Point - mVehiclePos; desiredDir.normalize(); //skip turning //return 0.0; } else { //rotate about the z axis so that we will turn twords racing line desiredDir.x = racelinedir.x*cos(turnAngle)+racelinedir.y*sin(turnAngle); desiredDir.y = racelinedir.x*-sin(turnAngle)+racelinedir.y*cos(turnAngle); desiredDir.z = 0; desiredDir.normalize(); } F32 dot = mDot(desiredDir,vehicledir); F32 turn; //This section of code could use some improvement //Try to have vehicledir match desiredDir //Don't turn if (dot>0.999) turn = 0; else if (dot>0.995f) turn = 0.2f; else if (dot>0.98f) turn = 0.4f; else if (dot>0.97f) turn = 0.6f; else if (dot>0.96f) turn = 0.8f; else turn = 1; F32 cz = desiredDir.x*vehicledir.y - desiredDir.y*vehicledir.x; if (cz<0) turn *=-1; return turn; } /** Calculates the max turn angle based on current speed that won't flip the vehicle currently does nothing */ F32 AIWheeledVehicleBrain::getMaxTurnAngle(F32 currentSpeed) { F32 maxTurnAngle = 50; return maxTurnAngle; } /** * Calculates the distance needed to slow down to a desired speed * */ F32 AIWheeledVehicleBrain::getSlowDownDistance(F32 currentspeed,F32 desiredspeed) { if (currentspeed<=desiredspeed) return 0.0; F32 distance = mvecStop[(int)currentspeed] - mvecStop[(int)desiredspeed]; return distance; } void AIWheeledVehicleBrain::setStartNode(int startnode) { mCurrentNode = startnode; SetLastNode(); setMoveDestination(mPath[mCurrentNode].m_Point); } int AIWheeledVehicleBrain::CalcNextNode(int node) { int nextnode = node+1; if (nextnode>mPath.size()-1) nextnode = 0; return nextnode; } int AIWheeledVehicleBrain::CalcPrevNode(int node) { int prevnode = node-1; if (prevnode<0) prevnode = mPath.size()-1; return prevnode; } void AIWheeledVehicleBrain::SetLastNode() { mLastNode = CalcPrevNode(mCurrentNode); } F32 AIWheeledVehicleBrain::avoidCollisions() { disableCollision(); F32 thetaScale = 200.0f; F32 safeDistance = (getVelocity() * thetaScale).magnitudeSafe(); F32 steering = 0.0; Point3F start = getPosition(); Point3F feeler,temp; F32 velocity = getVelocity().len() * thetaScale; Point3F ForwardRot,LeftRot,RightRot; getTransform().getColumn(0,&LeftRot); getTransform().getColumn(1,&ForwardRot); RightRot = -LeftRot; Point3F frontFeeler = (ForwardRot * velocity) + start; frontFeeler.z = start.z; Point3F leftFeeler = (LeftRot * velocity) + start; leftFeeler.z = start.z; Point3F rightFeeler = (RightRot * velocity) + start; rightFeeler.z = start.z; // Find closest intersection with wall (static shape) line if any bool intersectionFound = false; F32 closestDis; RayInfo closestRay; FeelerState closestFeelerState = Open; U32 avoidanceMask = STATIC_COLLISION_MASK|DAMAGEABLE_MASK; RayInfo rayInfo_1; closestRay.distance = rayInfo_1.distance = (frontFeeler-start).len(); if (getContainer()->castRay( start, frontFeeler, avoidanceMask , &rayInfo_1 )) { if (rayInfo_1.distance < closestRay.distance) { closestRay = rayInfo_1; intersectionFound = true; closestFeelerState = FrontFeeler; } } RayInfo rayInfo_2; rayInfo_2.distance = (leftFeeler-start).len(); if (getContainer()->castRay( start, leftFeeler, avoidanceMask , &rayInfo_2 )) { intersectionFound = true; if (rayInfo_2.distance < closestRay.distance) { closestRay = rayInfo_2; closestFeelerState = LeftFeeler; } } RayInfo rayInfo_3; rayInfo_3.distance = (rightFeeler-start).len(); if (getContainer()->castRay( start, rightFeeler, avoidanceMask , &rayInfo_3 )) { intersectionFound = true; if (rayInfo_3.distance < closestRay.distance) { closestRay = rayInfo_3; closestFeelerState = RightFeeler; } } if (!intersectionFound) { mMoveSpeed = 1.0; steering = 0.0; } else { switch (closestFeelerState){ case FrontFeeler: mMoveSpeed -= 0.05f; if (mMoveSpeed<0.01f) mMoveSpeed = 0.01f; case LeftFeeler: if (closestRay.distance >0) { steering = mDataBlock->maxSteeringAngle / (closestRay.distance / safeDistance); mMoveSpeed -= 0.01f; if (mMoveSpeed<0.01) mMoveSpeed = 0.01f; break; } case RightFeeler: if (closestRay.distance >0) { steering = -mDataBlock->maxSteeringAngle / (closestRay.distance / safeDistance); mMoveSpeed -= 0.01f; if (mMoveSpeed<0.01f) mMoveSpeed = 0.01f; break; } } } enableCollision(); return steering; } void AIWheeledVehicleBrain::updateVehicleData() { //Update vehicle data mVehiclePos = getPosition(); mCurrentSpeed = fabs(getVelocity().len()); MatrixF mat = getTransform(); VectorF vehicledir; vehicledir.set(0,1,0); mat.mulV(vehicledir); //ignore the z part because we can't fix that vehicledir.z = 0; vehicledir.normalize(); mVehicledir = vehicledir; Point3F pt1 = mPath[mCurrentNode].m_Point; Point3F pt2 = mPath[mLastNode].m_Point; mbBetweenLines = false; //Figure distance and intersection to racing line mTargetNode = mCurrentNode; bool b = true; if (!DistancePointToLine(mVehiclePos,pt2,pt1,mDistance,mIntersection)) { //Try the next node mTargetNode = CalcNextNode(mCurrentNode); pt1 = mPath[mTargetNode].m_Point; pt2 = mPath[mCurrentNode].m_Point; if (DistancePointToLine(mVehiclePos,pt2,pt1,mDistance,mIntersection)) { mLastNode = mCurrentNode; mCurrentNode = mTargetNode; mbBetweenLines = true; } else { //In between line segments Point3F inter = mPath[mTargetNode].m_Point; mDistance = getDistanceToNode(mTargetNode); mbBetweenLines = true; } } F32 distance = getDistanceToNode(mTargetNode); //pretty basic right now. need to add better conditions to being 'stuck' if (mCurrentSpeed < mStuckTolerance) { mStuckCount++; if (mStuckCount>mMaxStuckCount) { mStuckCycles++; if (mStuckCycles>mMaxStuckCount) { mStuckCount = 0; mStuckCycles = 0; Con::executef(this, 1, "onStuck"); } } else mMoveState = ModeMove; if (distance>mLostDistance) { mStuckCycles = 0; mStuckCount = 0; Con::executef(this, 1, "onLost"); } } else mStuckCycles = mStuckCount = 0; if (distance<mPrecision) { //Use the next node mTargetNode = CalcNextNode(mCurrentNode); pt1 = mPath[mTargetNode].m_Point; pt2 = mPath[mCurrentNode].m_Point; } VectorF racelinedir; racelinedir = pt1-pt2; racelinedir.normalize(); mRacelinedir = racelinedir; } // Think - figure out speed(accelerate or apply brakes) and steer the vehicle bool AIWheeledVehicleBrain::getAIMove(Move *movePtr) { if (!mbInit) return false; if (mDisableMove) { mRigid.setAtRest(); return true; } *movePtr = NullMove; updateVehicleData(); Point3F pt1 = mPath[mCurrentNode].m_Point; Point3F pt2 = mPath[mLastNode].m_Point; // Orient towards our destination. mMovecount++; if (mMovecount > mAvoidanceTick) { mMovecount = 1; movePtr->yaw = avoidCollisions(); movePtr->y = mMoveSpeed; } else { if (mMoveState == ModeMove || mMoveState == ModeReverse) { mTurnAngle = followRacingLine(); movePtr->yaw = mTurnAngle; } // Move towards the destination if (mMoveState == ModeMove) { movePtr->y = mMoveSpeed; setMoveDestination(mPath[mCurrentNode].m_Point); //Do we need to slow down or speed up? F32 targetSpeed = mPath[mCurrentNode].m_desiredSpeed; if (mCurrentSpeed>targetSpeed * mSAI->mAgression) { F32 distance = getDistanceToNode(mCurrentNode); F32 slowdistance = getSlowDownDistance(mCurrentSpeed,mPath[mCurrentNode].m_desiredSpeed); if (distance<(slowdistance+STOPSAFETY)) { movePtr->y = 0.0; movePtr->trigger[2] = true; } else if ((mPath[mCurrentNode].m_desiredSpeed>mCurrentSpeed)&&(mPath[mLastNode].m_desiredSpeed>mCurrentSpeed)) { movePtr->y = 0.0; movePtr->trigger[2] = true; } else { if ((mPath[mCurrentNode].m_desiredSpeed<999)&&(mPath[mLastNode].m_desiredSpeed<999)) { movePtr->y = mMoveSpeed; } else { movePtr->y = mMoveSpeed; } } } else { movePtr->y = mMoveSpeed; } } else if(mMoveState == ModeReverse) { movePtr->y = -1 * mMoveSpeed; } else if(mMoveState == ModeStop) { movePtr->y = 0; } } //Don't apply gas if vehicle is badly sliding Point3F vel = getVelocity(); vel.z = 0; vel.normalize(); F32 d = mDot(vel,mVehicledir); bool bSlide = false; if (mCurrentSpeed>400) { if (d<0.95) { movePtr->y = 0; movePtr->trigger[2] = true; } } // Replicate the trigger state into the move so that // triggers can be controlled from scripts. for( int i = 0; i < MaxTriggerKeys; i++ ) movePtr->trigger[i] = getImageTriggerState(i); return true; } F32 AIWheeledVehicleBrain::getDistanceToNode(U32 node) { Point3F v = mVehiclePos-mPath[node].m_Point; return v.len(); } /** * Utility function to throw callbacks. Callbacks always occure * on the datablock class. * * @param name Name of script function to call */ void AIWheeledVehicleBrain::throwCallback( const char *name ) { Con::executef(getDataBlock(), 2, name, scriptThis()); } //Perpendicular distance from point to line //returns false if the point is not perpendicular to line //Ignores the z value bool DistancePointToLine( Point3F Point, Point3F LineStart, Point3F LineEnd, F32 &distance, Point3F &Intersection ) { F32 LineMag; F32 U; //Ignore z value Intersection.set(0,0,0); Point.z = 0; LineEnd.z = 0; LineStart.z = 0; Point3F l = LineEnd-LineStart; LineMag = l.len(); if (LineMag!=0.0f) { U = ( ( ( Point.x - LineStart.x ) * ( LineEnd.x - LineStart.x ) ) + ( ( Point.y - LineStart.y ) * ( LineEnd.y - LineStart.y ) ))/ ( LineMag * LineMag ); } if( U < 0.0f || U > 1.0f ) return false; // closest point does not fall within the line segment Intersection.x = LineStart.x + U * ( LineEnd.x - LineStart.x ); Intersection.y = LineStart.y + U * ( LineEnd.y - LineStart.y ); Point3F l2 = Point - Intersection; distance = l2.len(); return true; } // -------------------------------------------------------------------------------------------- // Console Functions // -------------------------------------------------------------------------------------------- ConsoleMethod( AIWheeledVehicleBrain, init, void, 2, 2, "()" "Initialize AI for vehicle") { object->InitAI(); } ConsoleMethod( AIWheeledVehicleBrain, setStartNode, void, 3, 3, "( int startnode )" "Sets the move speed for an AI object.") { object->setStartNode( dAtoi( argv[2] ) ); } ConsoleMethod( AIWheeledVehicleBrain, setMoveTolerance, void, 3, 3, "(float speed)" "Sets the movetolerance") { object->setMoveTolerance(dAtof(argv[2])); } ConsoleMethod( AIWheeledVehicleBrain, addPathPoint, void, 4, 4, "(Point3F goal)(float speed)" "Add a point to the vehicle travel path") { float speed = 0; Point3F v( 0.0f, 0.0f, 0.0f ); dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z ); dSscanf( argv[3], "%f", &speed ); object->addPathPoint( v, speed ); } ConsoleMethod( AIWheeledVehicleBrain, getMoveDestination, const char *, 2, 2, "()" "Returns the point the AI is set to move to.") { Point3F movePoint = object->getMoveDestination(); char *returnBuffer = Con::getReturnBuffer( 256 ); dSprintf( returnBuffer, 256, "%f %f %f", movePoint.x, movePoint.y, movePoint.z ); return returnBuffer; } //============================================================================================== bool AIWheeledVehicleBrain::setSAI(SAI* dptr) { if (isGhost() || isProperlyAdded()) { if (mSAI != dptr) return onNewSAI(dptr); } else mSAI = dptr; return true; } bool AIWheeledVehicleBrain::onNewSAI(SAI* dptr) { mSAI = dptr; if (!mSAI) return false; setMaskBits(DataBlockMask); return true; } //---------------------------------------------------------------------------- ConsoleMethod( AIWheeledVehicleBrain, getSAI, S32, 2, 2, "()" "Return the SAI this AIWheeledVehicleBrain is using.") { return object->getSAI()? object->getSAI()->getId(): 0; } //---------------------------------------------------------------------------- ConsoleMethod(AIWheeledVehicleBrain, setSAI, bool, 3, 3, "(SAI db)" "Assign this AIWheeledVehicleBrain to use the specified SAI.") { SAI* data; if (Sim::findObject(argv[2],data)) { return object->setSAI(data); } Con::errorf("Could not find SAI Template \"%s\"!",argv[2]); return false; } Not working on a racing game this time around, so hope it helps. Edited July 14, 2016 by Azaezel Quote Link to comment Share on other sites More sharing options...
Azaezel Posted July 14, 2016 Share Posted July 14, 2016 original attribution: https://www.garagegames.com/community/resources/view/6222andhttps://www.garagegames.com/community/forums/viewthread/34604 Quote Link to comment Share on other sites More sharing options...
Jason Campbell Posted July 14, 2016 Share Posted July 14, 2016 That is very cool Azaezel! Plus I didn't notice that mirror until I clicked your link. The mirror is still alive and aiwheeledvehocle.zip is there. Thanks man. Quote Link to comment Share on other sites More sharing options...
flysouth Posted July 15, 2016 Author Share Posted July 15, 2016 Thanks for the code and links. Wow it looks like T3D can do very little unless you are prepared to mod the source code. :( Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.