Last active
October 25, 2017 04:55
-
-
Save makoConstruct/d8c1d8a4aea86d1ca8f1fbaf6a791078 to your computer and use it in GitHub Desktop.
Smoothly accelerates and decelerates to land exactly on target
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void updateApproacher(v2& currentPosition, v2& currentVelocity, v2 target, float delta, float acceleration, float deceleration, float maxSpeed){ | |
if(delta == 0) return; | |
v2 dto = target - currentPosition; | |
float dtm = dto.magnitude(); | |
float vm = currentVelocity.magnitude(); | |
auto distanceToStop = [&](float initialVelocity, float deceleration){ | |
float deltdec = deceleration*delta; | |
float numberOfFramesToSlowDown = max(0.0f, floor((initialVelocity - deltdec)/(deltdec))); | |
return numberOfFramesToSlowDown*delta*(initialVelocity - (deltdec*(numberOfFramesToSlowDown + 1))/2); | |
}; | |
if(vm < deceleration*delta && dtm < (vm + deceleration*delta)*delta){ | |
currentPosition = target; | |
currentVelocity = v2(0,0); | |
}else{ | |
float deltacc = delta*acceleration; | |
float deltdec = delta*deceleration; | |
v2 initialProjection; | |
v2 vn; | |
if(vm != 0){ | |
vn = currentVelocity/vm; | |
initialProjection = currentPosition + distanceToStop(vm, deceleration)*vn; | |
}else{ | |
initialProjection = currentPosition; | |
} | |
v2 targetRelativeToStoppingPoint = target - initialProjection; | |
float trm = targetRelativeToStoppingPoint.magnitude(); | |
if(trm != 0){ | |
v2 trn = targetRelativeToStoppingPoint/trm; | |
if(vm != 0){ | |
if(vn.dot(trn) < 0){ | |
currentVelocity = (currentVelocity - deltdec*vn).capLengthAt(maxSpeed); | |
}else{ //then we have some room for cleverness | |
//look over the range of reasonable alterations we could make. If one goes too far and the other goes not far enough, interpolate between them | |
v2 prospectiveFullAheadVelocity = (currentVelocity + deltacc*trn).capLengthAt(maxSpeed); | |
float pfavm = prospectiveFullAheadVelocity.magnitude(); | |
if(pfavm){ | |
v2 pfavn = prospectiveFullAheadVelocity/pfavm; | |
float frontDistanceToGo = pfavn.dot(dto); | |
float frontDistanceToStop = distanceToStop(pfavm, deceleration) + pfavm*delta; | |
if(frontDistanceToGo < frontDistanceToStop){ | |
//have to do interpolation | |
v2 prospectiveFullBehindVelocity = (currentVelocity - deltdec*vn).capLengthAt(maxSpeed); | |
float fbm = prospectiveFullBehindVelocity.magnitude(); | |
float behindDistanceToStop; | |
if(fbm){ | |
behindDistanceToStop = distanceToStop(fbm, deceleration) + fbm*delta; | |
}else{ | |
behindDistanceToStop = 0; | |
} | |
currentVelocity = lerp( | |
prospectiveFullBehindVelocity, | |
prospectiveFullAheadVelocity, | |
unlerpUnit(behindDistanceToStop, frontDistanceToStop, frontDistanceToGo) | |
); | |
}else{ | |
//don't have to do interpolation, prospectiveFullAheadVelocity is fine | |
currentVelocity = prospectiveFullAheadVelocity; | |
} | |
}else{ | |
currentVelocity = prospectiveFullAheadVelocity; | |
} | |
} | |
}else{ | |
v2 dtn = dto/dtm; | |
currentVelocity = currentVelocity + deltacc*dtn; | |
} | |
}else{ | |
currentVelocity = currentVelocity.shortenBy(deltdec); | |
} | |
currentPosition += currentVelocity*delta; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment