diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c06983a72..b0653c513 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -158,7 +158,7 @@ bool AIController::getAIMove(Move* movePtr) if (getGoal()->mObj.isValid()) getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); else if (getGoal()->mPosSet) - getNav()->setPathDestination(getGoal()->getPosition()); + getNav()->setPathDestination(getGoal()->getPosition(true)); } } else @@ -271,7 +271,7 @@ DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , "@brief Sets the move speed for an AI object.\n\n" "@param speed A speed multiplier between 0.0 and 1.0. " - "This is multiplied by the AIPlayer's base movement rates (as defined in " + "This is multiplied by the AIController controlled object's base movement rates (as defined in " "its PlayerData datablock)\n\n" "@see getMoveDestination()\n") @@ -290,7 +290,7 @@ DefineEngineMethod(AIController, getMoveSpeed, F32, (), , } DefineEngineMethod(AIController, stop, void, (), , - "@brief Tells the AIPlayer to stop moving.\n\n") + "@brief Tells the AIController controlled object to stop moving.\n\n") { object->mMovement.stopMove(); } @@ -514,28 +514,35 @@ void AIControllerData::initPersistFields() addFieldV("moveTolerance", TypeRangedF32, Offset(mMoveTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance from destination before stopping.\n\n" - "When the AIPlayer is moving to a given destination it will move to within " + "When the AIController controlled object is moving to a given destination it will move to within " "this distance of the destination and then stop. By providing this tolerance " - "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance from destination before stopping.\n\n" - "When the AIPlayer is moving to a given destination it will move to within " + "When the AIController controlled object is moving to a given destination it will move to within " "this distance of the destination and then stop. By providing this tolerance " - "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance tolerance on stuck check.\n\n" - "When the AIPlayer is moving to a given destination, if it ever moves less than " - "this tolerance during a single tick, the AIPlayer is considered stuck. At this point " + "When the AIController controlled object controlled object is moving to a given destination, if it ever moves less than " + "this tolerance during a single tick, the AIController controlled object is considered stuck. At this point " "the onMoveStuck() callback is called on the datablock.\n"); + addFieldV("HeightTolerance", TypeRangedF32, Offset(mHeightTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIController controlled object is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + addFieldV("moveStuckTestDelay", TypeRangedS32, Offset(mMoveStuckTestDelay, AIControllerData), &CommonValidators::PositiveInt, - "@brief The number of ticks to wait before testing if the AIPlayer is stuck.\n\n" - "When the AIPlayer is asked to move, this property is the number of ticks to wait " - "before the AIPlayer starts to check if it is stuck. This delay allows the AIPlayer " + "@brief The number of ticks to wait before testing if the AIController controlled object is stuck.\n\n" + "When the AIController controlled object is asked to move, this property is the number of ticks to wait " + "before the AIController controlled object starts to check if it is stuck. This delay allows the AIController controlled object " "to accelerate to full speed without its initial slow start being considered as stuck.\n" "@note Set to zero to have the stuck test start immediately.\n"); @@ -796,6 +803,22 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIFlyingVehicleControllerData); + +void AIFlyingVehicleControllerData::initPersistFields() +{ + docsURL; + addGroup("AI"); + + addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + "@brief Max height we can target."); + + addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + "@brief Max height we can target."); + + endGroup("AI"); + + Parent::initPersistFields(); +} // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) @@ -927,6 +950,31 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati } }; +void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a FlyingVehicle + + Point3F up = wvo->getTransform().getUpVector(); + up.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotPitch = mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = -dotPitch; +} + void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); @@ -942,6 +990,6 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca VehicleData* db = static_cast(wvo->getDataBlock()); movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; - movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; + movePtr->y = mMax(movePtr->y*1.1 - wvo->getSteering().y / db->maxSteeringAngle, 0.0f); } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 06b5e7ff3..eeaa13a94 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -236,17 +236,24 @@ class AIFlyingVehicleControllerData : public AIControllerData Right, Straight }; - + F32 mFlightFloor; + F32 mFlightCeiling; public: AIFlyingVehicleControllerData() { resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch); resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); mHeightTolerance = 200.0f; + mFlightCeiling = 200.0f; + mFlightFloor = 1.0; } + static void initPersistFields(); F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIFlyingVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index 2f0022658..f4a02e47f 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -52,10 +52,30 @@ AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) mPosSet = true; }; +Point3F AIInfo::getPosition(bool doCastray) +{ + Point3F pos = (mObj.isValid()) ? mObj->getPosition() : mPosition; + if (doCastray) + { + RayInfo info; + if (gServerContainer.castRay(pos, pos - Point3F(0, 0, getCtrl()->mControllerData->mHeightTolerance), StaticShapeObjectType, &info)) + { + pos = info.point; + } + } + + return pos; +} + F32 AIInfo::getDist() { AIInfo* controlObj = getCtrl()->getAIInfo(); - F32 ret = VectorF(controlObj->getPosition() - getPosition()).len(); + Point3F targPos = getPosition(); + + if (mFabs(targPos.z - controlObj->getPosition().z) < getCtrl()->mControllerData->mHeightTolerance) + targPos.z = controlObj->getPosition().z; + + F32 ret = VectorF(controlObj->getPosition() - targPos).len(); ret -= controlObj->mRadius + mRadius; return ret; } diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index e64bd38d3..8aede0cae 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -36,7 +36,7 @@ struct AIInfo Point3F mPosition, mLastPos; bool mPosSet; F32 mRadius; - Point3F getPosition() { return (mObj.isValid()) ? mObj->getPosition() : mPosition; } + Point3F getPosition(bool doCastray = false); F32 getDist(); AIInfo() = delete; AIInfo(AIController* controller); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 0cb42d998..ff3e25b22 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -134,11 +134,11 @@ void AINavigation::repath() else { // If we're following, get their position. - mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(true); } // Update from position and replan. - mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); + mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(true); mPathData.path->plan(); // Move to first node (skip start pos). @@ -215,8 +215,8 @@ bool AINavigation::setPathDestination(const Point3F& pos, bool replace) NavPath* path = new NavPath(); path->mMesh = mNavMesh; - path->mFrom = getCtrl()->getAIInfo()->getPosition(); - path->mTo = getCtrl()->getGoal()->getPosition(); + path->mFrom = getCtrl()->getAIInfo()->getPosition(true); + path->mTo = getCtrl()->getGoal()->getPosition(true); path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; @@ -257,7 +257,7 @@ void AINavigation::followObject() if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) return; - if (setPathDestination(getCtrl()->getGoal()->getPosition())) + if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) { getCtrl()->clearCover(); }