void plDynamicWalkingStrategy::Apply(float delSecs) { hsVector3 velocity = fController->GetLinearVelocity(); hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); // Add in gravity if the avatar's z velocity isn't being set explicitly if (hsABS(velocity.fZ) < 0.001f) { // Get our previous z velocity. If we're on the ground, clamp it to zero at // the largest, so we won't launch into the air if we're running uphill. float prevZVel = achievedVelocity.fZ; if (IsOnGround()) prevZVel = hsMinimum(prevZVel, 0.f); velocity.fZ = prevZVel + (kGravity * delSecs); } if (velocity.fZ < kTerminalVelocity) velocity.fZ = kTerminalVelocity; fController->SetPushingPhysical(nil); fController->SetFacingPushingPhysical(false); fGroundHit = fFalseGround = false; float groundZVelocity; if (ICheckForGround(groundZVelocity)) velocity.fZ += groundZVelocity; fController->SetLinearVelocitySim(velocity); }
void cMonster::MoveToWayPoint(cChunk & a_Chunk) { if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) { return; } if (m_JumpCoolDown == 0) { if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) { if (((IsOnGround()) && (GetSpeed().SqrLength() == 0.0f)) || (IsSwimming())) { m_bOnGround = false; m_JumpCoolDown = 20; // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport AddPosY(1.6); // Jump!! SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed. SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error and 1.6 and 3.2 are perfect. } } } else { --m_JumpCoolDown; } Vector3d Distance = m_NextWayPointPosition - GetPosition(); if ((Distance.x != 0.0f) || (Distance.z != 0.0f)) { Distance.y = 0; Distance.Normalize(); if (m_bOnGround) { Distance *= 2.5f; } else if (IsSwimming()) { Distance *= 1.3f; } else { // Don't let the mob move too much if he's falling. Distance *= 0.25f; } // Apply walk speed: Distance *= m_RelativeWalkSpeed; /* Reduced default speed. Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence better pathfinding. */ Distance *= 0.5; AddSpeedX(Distance.x); AddSpeedZ(Distance.z); } }
void plWalkingStrategy::Update(float delSecs) { if (fGroundHit || fFalseGround) fTimeInAir = 0.0f; else { fTimeInAir += delSecs; if (fHeadHit) { // If we're airborne and hit our head, override achieved velocity to avoid being shoved sideways hsVector3 velocity = fController->GetLinearVelocity(); hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); achievedVelocity.fX = velocity.fX; achievedVelocity.fY = velocity.fY; if (achievedVelocity.fZ > 0.0f) achievedVelocity.fZ = 0.0f; fController->OverrideAchievedLinearVelocity(achievedVelocity); } } hsVector3 zeroVelocity(0.f, 0.f, 0.f); fController->SetLinearVelocity(zeroVelocity); if (!fHitGroundInThisAge && IsOnGround()) fHitGroundInThisAge = true; if (fClearImpact) { fImpactTime = 0.0f; fImpactVelocity.Set(0.0f, 0.0f, 0.0f); } if (IsOnGround()) fClearImpact = true; else { fImpactTime = fTimeInAir; fImpactVelocity = fController->GetAchievedLinearVelocity(); // convert orientation from subworld to avatar-local coordinates fImpactVelocity = (hsVector3)fController->GetLocalRotation().Rotate(&fImpactVelocity); fClearImpact = false; } }
void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) { StatValue Value = (StatValue)floor(a_DeltaPos.Length() * 100 + 0.5); if (m_AttachedTo == NULL) { if (IsClimbing()) { if (a_DeltaPos.y > 0.0) // Going up { m_Stats.AddValue(statDistClimbed, (StatValue)floor(a_DeltaPos.y * 100 + 0.5)); } } else if (IsSubmerged()) { m_Stats.AddValue(statDistDove, Value); AddFoodExhaustion(0.00015 * (double)Value); } else if (IsSwimming()) { m_Stats.AddValue(statDistSwum, Value); AddFoodExhaustion(0.00015 * (double)Value); } else if (IsOnGround()) { m_Stats.AddValue(statDistWalked, Value); AddFoodExhaustion((m_IsSprinting ? 0.001 : 0.0001) * (double)Value); } else { if (Value >= 25) // Ignore small/slow movement { m_Stats.AddValue(statDistFlown, Value); } } } else { switch (m_AttachedTo->GetEntityType()) { case cEntity::etMinecart: m_Stats.AddValue(statDistMinecart, Value); break; case cEntity::etBoat: m_Stats.AddValue(statDistBoat, Value); break; case cEntity::etMonster: { cMonster * Monster = (cMonster *)m_AttachedTo; switch (Monster->GetMobType()) { case cMonster::mtPig: m_Stats.AddValue(statDistPig, Value); break; case cMonster::mtHorse: m_Stats.AddValue(statDistHorse, Value); break; default: break; } break; } default: break; } } }
void psLinearMovement::GetDRData (bool& on_ground, csVector3& pos, float& yrot, iSector*& sector, csVector3& vel, csVector3& worldVel, float& ang_vel) { on_ground = IsOnGround (); GetLastPosition (pos, yrot, sector); vel = velBody; ang_vel = angularVelocity.y; worldVel = this->velWorld; }
void plWalkingStrategy::RecalcVelocity(double timeNow, float elapsed, bool useAnim) { if (fControlledFlight != 0) { if (IsOnGround()) fControlledFlightTime = fTimeInAir; if (fControlledFlightTime > kControlledFlightThreshold) EnableControlledFlight(false); } plAnimatedMovementStrategy::RecalcVelocity(timeNow, elapsed, useAnim); }
DDWORD FlagObject::EngineMessageFn(DDWORD messageID, void *pData, DFLOAT fData) { // Handle the engine messages we're interested in... switch(messageID) { case MID_PRECREATE: { if (fData == PRECREATE_WORLDFILE || fData == PRECREATE_STRINGPROP) { ReadProp((ObjectCreateStruct*)pData); } PostPropRead((ObjectCreateStruct*)pData); break; } case MID_INITIALUPDATE: { OnInitialUpdate(pData, fData); break; } case MID_TOUCHNOTIFY: { if (!IsWithPlayer()) { OnTouchNotify((HOBJECT)pData); } break; } case MID_UPDATE: { if (IsOnGround()) { ReturnToFlagStand(); } break; } } return B2BaseClass::EngineMessageFn(messageID, pData, fData); }
// Do the actual move int psLinearMovement::MoveV (float delta) { if (velBody < SMALL_EPSILON && velWorld < SMALL_EPSILON && (!colldet || colldet->IsOnGround ())) return PS_MOVE_DONTMOVE; // didn't move anywhere int ret = PS_MOVE_SUCCEED; iMovable* movable = mesh->GetMovable (); if (movable->GetSectors ()->GetCount () <= 0) return PS_MOVE_DONTMOVE; // didn't move anywhere csMatrix3 mat; // To test collision detection we use absolute position and transformation // (this is relevant if we are anchored). Later on we will correct that. csReversibleTransform fulltransf = movable->GetFullTransform (); mat = fulltransf.GetT2O (); csVector3 worldVel (fulltransf.This2OtherRelative (velBody) + velWorld); csVector3 oldpos (fulltransf.GetOrigin ()); csVector3 newpos (worldVel*delta + oldpos); csVector3 bufpos = newpos; float dist = (newpos - oldpos).Norm (); // @@@ Magodra: In some cases the newpos seams to be invalid. Not sure about // the reason, but the FollowSegment function will not work later // in this function. So check for that condidtion, give warning, // and halt the movement. if (CS::IsNaN(newpos.x) || CS::IsNaN(newpos.y) || CS::IsNaN(newpos.z)) { printf("From old position %s ",toString(oldpos,movable->GetSectors()->Get(0)).GetDataSafe()); StackTrace("LinearMovement to a NAN position."); return PS_MOVE_DONTMOVE; // didn't move anywhere } // Check for collisions and adjust position if (colldet) { if (!colldet->AdjustForCollisions (oldpos, newpos, worldVel, delta, movable)) { ret = PS_MOVE_FAIL; newpos = oldpos; } else { // check if we collided, did move less than 9/10 of the distance if ((newpos - bufpos).Norm () > dist/10.0) { ret = PS_MOVE_PARTIAL; } } } csVector3 origNewpos = newpos; bool mirror = false; // Update position to account for portals iSector* new_sector = movable->GetSectors ()->Get (0); iSector* old_sector = new_sector; // @@@ Jorrit: had to do this add! // We need to measure slightly above the position of the actor or else // we won't really cross a portal. float height5 = (bottomSize.y + topSize.y) / 10.0f; newpos.y += height5; csMatrix3 id; csOrthoTransform transform_oldpos (id, oldpos + csVector3 (0.0f, height5, 0.0f)); new_sector = new_sector->FollowSegment (transform_oldpos, newpos, mirror, PS_LINMOVE_FOLLOW_ONLY_PORTALS); newpos.y -= height5; if (new_sector != old_sector) movable->SetSector (new_sector); portalDisplaced += newpos - origNewpos; if(!IsOnGround ()) { //printf("Applying gravity: velY: %g.\n", velWorld.y); // gravity! move down! velWorld.y -= gravity * delta; /* * Terminal velocity * ((120 miles/hour / 3600 second/hour) * 5280 feet/mile) * / 3.28 feet/meter = 53.65 m/s */ // The body velocity is figured in here too. if (velWorld.y < 0) { if (fulltransf.This2OtherRelative (velBody).y + velWorld.y < -(ABS_MAX_FREEFALL_VELOCITY)) velWorld.y = -(ABS_MAX_FREEFALL_VELOCITY) - fulltransf.This2OtherRelative (velBody).y; if (velWorld.y > 0) { // printf("Reset other y %g\n", fulltransf.This2OtherRelative (velBody).y); velWorld.y = 0; } } } else { if(velWorld.y < 0) { velWorld.y = 0; } if (hugGround) HugGround (newpos, new_sector); } // Move to the new position. If we have an anchor we have to convert // the new position from absolute to relative. movable->GetTransform ().SetOrigin (newpos); movable->GetTransform ().SetT2O( movable->GetTransform ().GetT2O () * transform_oldpos.GetT2O ()); if (colldet) { // Part 4: Add us to all nearby sectors. mesh->PlaceMesh (); } movable->UpdateMove (); return ret; }
void cMonster::HandleFalling() { m_bTouchGround = IsOnGround(); super::HandleFalling(); }
void CHostageImprov::MoveTowards(const Vector &pos, float deltaT) { Vector move; float_precision accelRate; const float crouchWalkRate = 250.0f; // Jump up on ledges // Because we may not be able to get to our goal position and enter the next // area because our extent collides with a nearby vertical ledge, make sure // we look far enough ahead to avoid this situation. // Can't look too far ahead, or bots will try to jump up slopes. // // NOTE: We need to do this frequently to catch edges at the right time // TODO: Look ahead *along path* instead of straight line ClearPath(); if ((m_lastKnownArea == NULL || !(m_lastKnownArea->GetAttributes() & NAV_NO_JUMP)) && !IsUsingLadder() && !IsJumping() && IsOnGround() && !IsCrouching()) { float ground; Vector aheadRay(pos.x - GetFeet().x, pos.y - GetFeet().y, 0); aheadRay.NormalizeInPlace(); bool jumped = false; if (IsRunning()) { const float farLookAheadRange = 80.0f; Vector normal; Vector stepAhead = GetFeet() + farLookAheadRange * aheadRay; stepAhead.z += HumanHeight; if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground, &normal )) { if (normal.z > 0.9f) jumped = DiscontinuityJump(ground, HOSTAGE_ONLY_JUMP_DOWN); } } if (!jumped) { // close up jumping // cant be less or will miss jumps over low walls const float lookAheadRange = 30.0f; Vector stepAhead = GetFeet() + lookAheadRange * aheadRay; stepAhead.z += HumanHeight; if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground)) { jumped = DiscontinuityJump(ground); } } if (!jumped) { // about to fall gap-jumping const float lookAheadRange = 10.0f; Vector stepAhead = GetFeet() + lookAheadRange * aheadRay; stepAhead.z += HumanHeight; if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground)) { jumped = DiscontinuityJump(ground, HOSTAGE_ONLY_JUMP_DOWN, HOSTAGE_MUST_JUMP); } } } move = (pos - GetFeet()); move.z = 0; if (!move.IsZero()) { move.NormalizeInPlace(); } switch (m_moveType) { case Stopped: accelRate = 0; break; case Walking: if (IsCrouching()) accelRate = crouchWalkRate; else accelRate = 400; break; case Running: if (IsCrouching()) accelRate = crouchWalkRate; else accelRate = 1000; break; } m_vel.x = move.x * accelRate * deltaT + m_vel.x; m_vel.y = move.y * accelRate * deltaT + m_vel.y; }
void CHostageImprov::__MAKE_VHOOK(OnUpdate)(float deltaT) { if (!IsAlive() || cv_hostage_stop.value > 0.0f) return; if (m_blinkTimer.IsElapsed()) { if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid) { m_blinkTimer.Start(RANDOM_FLOAT(3, 10)); m_blinkCounter = RANDOM_LONG(2, 4); } else { m_blinkTimer.Start(RANDOM_FLOAT(0.5f, 2.0f)); m_blinkCounter = RANDOM_LONG(1, 2); } } if (m_blinkCounter) { m_hostage->pev->body = 1; --m_blinkCounter; } else { m_hostage->pev->body = 0; } UpdateGrenadeReactions(); UpdateDelayedChatter(); UpdateVision(); m_behavior.Update(); m_actualVel.x = m_hostage->pev->origin.x - m_lastPosition.x; m_actualVel.y = m_hostage->pev->origin.y - m_lastPosition.y; const float runSpeed = 289.0f; const float walkSpeed = 9.0f; const float fallVelocity = -1000.0f; const float safeTime = 0.4f; if (IsOnGround()) { if (IsCrouching()) { if (m_actualVel.LengthSquared() > 9.0f) { if (m_animateState.GetPerformance() != HostageAnimateState::CrouchWalk) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::CrouchWalk); ClearLookAt(); if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid) m_animateState.AddSequence(this, ACT_CROUCH_WALK, 99.9, 2.0); else m_animateState.AddSequence(this, ACT_CROUCH_WALK_SCARED, 99.9, 2.0); } } else if (m_animateState.GetPerformance() != HostageAnimateState::Crouch) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Crouch); if (m_scaredTimer.IsElapsed()) m_animateState.AddSequence(this, ACT_CROUCH_IDLE, 99.9); else m_animateState.AddSequence(this, ACT_CROUCH_IDLE_SCARED); } } else { UTIL_MakeVectors(m_hostage->pev->angles); float dot = DotProduct2D(gpGlobals->v_forward, m_actualVel); if (dot < -3.0f) { if (m_animateState.GetPerformance() != HostageAnimateState::Walk) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Walk); ClearLookAt(); float speed; if (m_actualVel.LengthSquared() > runSpeed) speed = 2.0f; else speed = 1.0f; m_animateState.AddSequence(this, ACT_WALK_BACK, 99.9, speed); } } else { if (m_actualVel.LengthSquared() > runSpeed) { if (m_animateState.GetPerformance() != HostageAnimateState::Run) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Run); ClearLookAt(); if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid && !m_behavior.IsState(&m_escapeState)) m_animateState.AddSequence(this, ACT_RUN, 99.9, 2.0); else m_animateState.AddSequence(this, ACT_RUN_SCARED, 99.9, 2.0); } } else if (m_actualVel.LengthSquared() > walkSpeed) { if (m_animateState.GetPerformance() != HostageAnimateState::Walk) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Walk); ClearLookAt(); if (m_behavior.IsState(&m_escapeState)) { m_animateState.AddSequence(this, ACT_WALK_SNEAKY, 99.9, 1.5); } else if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid) { m_animateState.AddSequence(this, ACT_WALK, 99.9, 1.5); } else m_animateState.AddSequence(this, ACT_WALK_SCARED, 99.9, 1.5); } } else { if (m_animateState.GetPerformance() == HostageAnimateState::Walk || m_animateState.GetPerformance() == HostageAnimateState::Run) m_animateState.Reset(); UpdateStationaryAnimation(); } } } } else if (m_hostage->pev->velocity.z < fallVelocity && m_animateState.GetPerformance() != HostageAnimateState::Fall) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Fall); m_animateState.AddSequence(this, ACT_FALL, 99.9); } if (!m_collisionTimer.HasStarted() || m_collisionTimer.IsGreaterThen(safeTime)) SetKnownGoodPosition(m_lastPosition); m_lastPosition = m_hostage->pev->origin; m_animateState.OnUpdate(this); }
void plRidingAnimatedPhysicalStrategy::Apply(float delSecs) { hsVector3 LinearVelocity=fCore->GetLinearVelocity(); hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); if (fCore->IsKinematic()) { //want to make sure nothing funky happens in the sim IApplyKinematic(); return; } if (!fCore->IsEnabled()) return; //need to sweep ahead to see what we might hit. // if we hit anything we should probably apply the force that would normally be applied in fCore->SetPushingPhysical(nil); fCore->SetFacingPushingPhysical( false); plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); hsPoint3 startPos, desiredDestination, endPos; fCore->GetPositionSim(startPos); uint32_t collideFlags = 1<<plSimDefs::kGroupStatic | 1<<plSimDefs::kGroupAvatarBlocker | 1<<plSimDefs::kGroupDynamic; std::multiset<plControllerSweepRecord> GroundHitRecords; int possiblePlatformCount =fCore->SweepControllerPath(startPos, startPos + hsPoint3(0.f,0.f, -0.002f), true, true, collideFlags, GroundHitRecords); float maxPlatformVel = - FLT_MAX; int platformCount=0; fGroundHit = false; if(possiblePlatformCount) { std::multiset<plControllerSweepRecord>::iterator curRecord; for(curRecord = GroundHitRecords.begin(); curRecord != GroundHitRecords.end(); curRecord++) { hsBool groundlike=false; if((curRecord->locHit.fZ - startPos.fZ)<= .2) groundlike= true; if(groundlike) { if(curRecord->ObjHit !=nil) { hsVector3 vel; curRecord->ObjHit->GetLinearVelocitySim(vel); if(vel.fZ > maxPlatformVel) { maxPlatformVel= vel.fZ; } } platformCount ++; fGroundHit = true; } } } bool gotGroundHit = fGroundHit; if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) fCore->SetGlobalLoc(l2w); // Convert our avatar relative velocity to subworld relative if (!LinearVelocity.IsEmpty()) { LinearVelocity = l2w * LinearVelocity; const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); if (subworldCI) LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; } if(!IsOnGround()) { if(!fNeedVelocityOverride) { LinearVelocity.fZ= AchievedLinearVelocity.fZ; } else { LinearVelocity = fOverrideVelocity; } } if(fStartJump) { LinearVelocity.fZ =12.0f; } if(platformCount) { LinearVelocity.fZ = LinearVelocity.fZ + maxPlatformVel; } //probably neeed to do something with contact normals in here //for false ground stuff fFalseGround = false; hsVector3 testLength = LinearVelocity * delSecs + hsVector3(0.0, 0.0, -0.00f); // hsPoint3 desiredDestination= startPos + testLength; if(!IsOnGround()) { if(ICheckMove(startPos, desiredDestination)) {//we can get there soley by the LinearVelocity fNeedVelocityOverride =false; } else { fNeedVelocityOverride =true; fOverrideVelocity = LinearVelocity; fOverrideVelocity.fZ -= delSecs * 32.f; } } else { fNeedVelocityOverride =false; } fCore->SetLinearVelocity(LinearVelocity); } }
/////////////////////////// //Walking Strategy void plWalkingStrategy::Apply(float delSecs) { //Apply Should Only be Called from a PhysicalControllerCore hsAssert(fCore,"No Core shouldn't be Applying"); uint32_t collideFlags = 1<<plSimDefs::kGroupStatic | 1<<plSimDefs::kGroupAvatarBlocker | 1<<plSimDefs::kGroupDynamic; if(!fCore->IsSeeking()) { collideFlags|=(1<<plSimDefs::kGroupExcludeRegion); } bool OnTopOfAnimatedPhys=false; hsVector3 LinearVelocity=fCore->GetLinearVelocity(); hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); hsPoint3 positionBegin; fCore->GetPositionSim(positionBegin); bool recovered=false; if (fCore->IsKinematic()) { plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) { fCore->SetKinematicLoc(l2w); fCore->SetGlobalLoc(l2w); } } return; } if (!fCore->IsEnabled()) return; bool gotGroundHit = fGroundHit; fGroundHit = false; fCore->SetPushingPhysical(nil); fCore->SetFacingPushingPhysical( false); plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { static const float kGravity = -32.f; // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) fCore->SetGlobalLoc(l2w); // Convert our avatar relative velocity to subworld relative if (!LinearVelocity.IsEmpty()) { LinearVelocity = l2w * LinearVelocity; const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); if (subworldCI) LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; } // Add in gravity if the avatar's z velocity isn't being set explicitly // (Add in a little fudge factor, since the animations usually add a // tiny bit of z.) if (hsABS(LinearVelocity.fZ) < 0.001f) { // Get our previous z velocity. If we're on the ground, clamp it to zero at // the largest, so we won't launch into the air if we're running uphill. float prevZVel = AchievedLinearVelocity.fZ; if (IsOnGround()) prevZVel = hsMinimum(prevZVel, 0.f); float grav = kGravity * delSecs; // If our gravity contribution isn't high enough this frame, we won't // report a collision even when standing on solid ground. float maxGrav = -.001f / delSecs; if (grav > maxGrav) grav = maxGrav; LinearVelocity.fZ = prevZVel + grav; } // If we're airborne and the velocity isn't set, use the velocity from // the last frame so we maintain momentum. if (!IsOnGround() && LinearVelocity.fX == 0.f && LinearVelocity.fY == 0.f) { LinearVelocity.fX = AchievedLinearVelocity.fX; LinearVelocity.fY = AchievedLinearVelocity.fY; } //make terminal velocity equal to k. it is wrong but has been this way and //don't want to break any puzzles. on top of that it will reduce tunneling behavior if(LinearVelocity.fZ<kGravity)LinearVelocity.fZ=kGravity; fCore->SetLinearVelocity(LinearVelocity); // Scale the velocity to our actual step size (by default it's feet/sec) hsVector3 vel(LinearVelocity.fX * delSecs, LinearVelocity.fY * delSecs, LinearVelocity.fZ * delSecs); unsigned int colFlags = 0; fGroundHit = false; fFalseGround = false; fContactNormals.Swap(fPrevSlidingNormals); fContactNormals.SetCount(0); fCore->Move(vel, collideFlags, colFlags); ICheckForFalseGround(); //if(fReqMove2) fCore->Move2(vel); /*If the Physx controller thinks we have a collision from below, need to make sure we have at least have false ground, otherwise Autostepping can send us into the air, and we will some times float/panic link. For some reason the NxControllerHitReport does not always send messages regarding Controller contact with ground plane, but will (almost) always return NXCC_COLLISION_DOWN with the move method. */ if((colFlags&kBottom ) &&(fGroundHit==false)) { fFalseGround=true; } if(colFlags&kTop) { fHitHead=true; //Did you hit your head on a dynamic? //with Physx's wonderful controller hit report vs flags issues we need to actually sweep to see std::multiset< plControllerSweepRecord > HitsDynamic; uint32_t testFlag=1<<plSimDefs::kGroupDynamic; hsPoint3 startPos; hsPoint3 endPos; fCore->GetPositionSim(startPos); endPos= startPos + vel; int NumObjsHit=fCore->SweepControllerPath(startPos, endPos, true, false, testFlag, HitsDynamic); if(NumObjsHit>0) { for(std::multiset< plControllerSweepRecord >::iterator curObj= HitsDynamic.begin(); curObj!=HitsDynamic.end(); curObj++) { hsAssert(curObj->ObjHit,"We allegedly hit something, but there is no plasma physical associated with it"); if(curObj->ObjHit) {//really we shouldn't have to check hitObj should be nil only if we miss, or the physX object //doesn't have a user data associated with this either way this just shouldn't happen hsVector3 hitObjVel; curObj->ObjHit->GetLinearVelocitySim(hitObjVel); hsVector3 relativevel=LinearVelocity-hitObjVel; curObj->ObjHit->SetHitForce(relativevel * 10.0f * (*curObj).ObjHit->GetMass(), (*curObj).locHit); } } HitsDynamic.clear(); } } } }
void plWalkingStrategy::Apply(float delSecs) { hsVector3 velocity = fController->GetLinearVelocity(); hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); // Add in gravity if the avatar's z velocity isn't being set explicitly if (hsABS(velocity.fZ) < 0.001f) { // Get our previous z velocity. If we're on the ground, clamp it to zero at // the largest, so we won't launch into the air if we're running uphill. float prevZVel = achievedVelocity.fZ; if (IsOnGround()) prevZVel = hsMinimum(prevZVel, 0.0f); velocity.fZ = prevZVel + (kGravity * delSecs); } // If we're airborne and the velocity isn't set, use the velocity from // the last frame so we maintain momentum. if (!IsOnGround() && velocity.fX == 0.0f && velocity.fY == 0.0f) { velocity.fX = achievedVelocity.fX; velocity.fY = achievedVelocity.fY; } if (!fGroundHit && fSlidingNormals.Count()) { // We're not on solid ground, so we should be sliding against whatever // we're hitting (like a rock cliff). Each vector in fSlidingNormals is // the surface normal of a collision that's too steep to be ground, so // we project our current velocity onto that plane and slide along the // wall. // // Also, sometimes PhysX reports a bunch of collisions from the wall, // but nothing from underneath (when there should be). So if we're not // touching ground, we offset the avatar in the direction of the // surface normal(s). This doesn't fix the issue 100%, but it's a hell // of a lot better than nothing, and suitable duct tape until a future // PhysX revision fixes the issue. // // Yes, there's room for optimization here if we care. hsVector3 offset(0.0f, 0.0f, 0.0f); for (int i = 0; i < fSlidingNormals.GetCount(); i++) { offset += fSlidingNormals[i]; hsVector3 velNorm = velocity; if (velNorm.MagnitudeSquared() > 0.0f) velNorm.Normalize(); if (velNorm * fSlidingNormals[i] < 0.0f) { hsVector3 proj = (velNorm % fSlidingNormals[i]) % fSlidingNormals[i]; if (velNorm * proj < 0.0f) proj *= -1.0f; velocity = velocity.Magnitude() * proj; } } if (offset.MagnitudeSquared() > 0.0f) { // 5 ft/sec is roughly the speed we walk backwards. // The higher the value, the less likely you'll trip // the bug, and this seems reasonable. offset.Normalize(); velocity += offset * 5.0f; } } if (velocity.fZ < kTerminalVelocity) velocity.fZ = kTerminalVelocity; // Convert to a displacement vector hsVector3 displacement = velocity * delSecs; // Reset vars and move the controller fController->SetPushingPhysical(nil); fController->SetFacingPushingPhysical(false); fGroundHit = fFalseGround = fHeadHit = false; fSlidingNormals.SetCount(0); unsigned int collideResults = 0; unsigned int collideFlags = 1<<plSimDefs::kGroupStatic | 1<<plSimDefs::kGroupAvatarBlocker | 1<<plSimDefs::kGroupDynamic; if (!fController->IsSeeking()) collideFlags |= (1<<plSimDefs::kGroupExcludeRegion); fController->Move(displacement, collideFlags, collideResults); if ((!fGroundHit) && (collideResults & kBottom)) fFalseGround = true; if (collideResults & kTop) fHeadHit = true; }
void ControllableObject::Update(float msec, set<Action> actions) { bool moveStatusBg = isMoving; Renderer* r = &Renderer::Instance(); if (actions.find(MOVE_CAM_FORWARD) != actions.end()) { isMoving = true; //if (fwDir.Length() == 0) fwDir = r->GetCamDirection(); body->setActivationState(1); body->applyCentralForce(btVector3(fwDir.x * 500, fwDir.y * 500, fwDir.z * 500)); } else { fwDir = Vector3(0, 0, 0); } if (actions.find(MOVE_CAM_BACKWARD) != actions.end()) { isMoving = true; //if (bwDir.Length() == 0) bwDir = r->GetCamDirection().Inverse(); body->setActivationState(1); body->applyCentralForce(btVector3(bwDir.x * 500, bwDir.y * 500, bwDir.z * 500)); } else { bwDir = Vector3(0, 0, 0); } if (actions.find(MOVE_CAM_LEFT) != actions.end()) { isMoving = true; //if (lfDir.Length() == 0) lfDir = r->GetCamLeft().Inverse(); body->setActivationState(1); body->applyCentralForce(btVector3(lfDir.x * 500, lfDir.y * 500, lfDir.z * 500)); } else { lfDir = Vector3(0, 0, 0); } if (actions.find(MOVE_CAM_RIGHT) != actions.end()) { isMoving = true; //if (rtDir.Length() == 0) rtDir = r->GetCamLeft(); body->setActivationState(1); body->applyCentralForce(btVector3(rtDir.x * 500, rtDir.y * 500, rtDir.z * 500)); } else { rtDir = Vector3(0, 0, 0); } if ((actions.find(JUMP) != actions.end()) && (IsOnGround())) { isMoving = true; if (!Controller::Instance().IsContinuous(JUMP)) { //if (upDir.Length() == 0) upDir = r->GetCamUp(); body->setActivationState(1); body->applyCentralForce(btVector3(upDir.x * 20000, upDir.y * 20000, upDir.z * 20000)); ResourceManager::Instance().GetResource<SoundEffectResource>("jumping.wav")->Play(); } } else { upDir = Vector3(0, 0, 0); } if (((GetPosition() - GetPreviousPosition()).Length() == 0) && !((moveStatusBg == false) && (isMoving == true))) { isMoving = false; } PhysicsObject::Update(msec, actions); }