Skip to content

Instantly share code, notes, and snippets.

@makoConstruct
Last active October 25, 2017 04:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makoConstruct/d8c1d8a4aea86d1ca8f1fbaf6a791078 to your computer and use it in GitHub Desktop.
Save makoConstruct/d8c1d8a4aea86d1ca8f1fbaf6a791078 to your computer and use it in GitHub Desktop.
Smoothly accelerates and decelerates to land exactly on target
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