I am trying to implement the game similar to Dota 2 or Warcraft 3. I want this: when user clicks the right mouse button I want to navigate to the click position. However if there is an obstacle in my way I want to go around it.
Here's what I've got so far:
// p - where user clicked
// stepSize - something like movement speed
// minDistance - not important for this problem
// objects - all the colliding objects
// shouldStepInto - not important for this problem
public boolean moveTowardsPoint(Point p, double stepSize, double minDistance, ArrayList<GameBodyObject> objects, boolean shouldStepInto) {
ArrayList<GameBodyObject> others = (ArrayList<GameBodyObject>) objects.clone();
others.remove(this);
double dx = p.getX() - getPosition().getX();
double dy = p.getY() - getPosition().getY();
boolean isClose = false;
Point lastPosition = getPosition().clone();
if (Math.sqrt(dx * dx + dy * dy) < Math.max(minDistance, stepSize)) {
isClose = true;
} else {
setRotation(Math.atan2(dy, dx));
// Though the sqrt is expensive operation
// We check first without using sqrt
if (shouldStepInto && Math.abs(dx) + Math.abs(dy) < stepSize) {
if (Math.sqrt(dx * dx + dy * dy) < stepSize) {
getPosition().setX(p.getX());
getPosition().setY(p.getY());
}
} else {
getPosition().setX(getPosition().getX() + (stepSize * Math.cos(getRotation())));
getPosition().setY(getPosition().getY() + (stepSize * Math.sin(getRotation())));
}
}
// -- COLLISION AVOIDANCE -- //
ArrayList<Double> movements = new ArrayList<>();
for (GameBodyObject obj : others) {
// Check collision with all the other GameBodyObjects
if (checkCollision(obj)) {
// How much have I moved lastly
double distanceMoved = getPosition().distance(lastPosition);
if(!isStatic() && distanceMoved > 0.01) {
// calculate vector between me and a collider
Vector v = new Vector(getPosition().getX() - obj.getPosition().getX(), getPosition().getY() - obj.getPosition().getY());
// Calculate 2 normal vectors
Vector n1 = v.getNormal(stepSize, 1);
Vector n2 = v.getNormal(stepSize, -1);
Point p1 = getPosition().clone();
Point p2 = getPosition().clone();
// Try to move in the directions of two normal vectors
p1.moveByVector(n1);
p2.moveByVector(n2);
double dist1 = p1.distance(p);
double dist2 = p2.distance(p);
// If I got closer with normal vector n1
if (dist1 < dist2) {
// Store the angle
movements.add(n1.getAngle());
// Otherwise if I got closer with normal vector n2
} else {
// store the angle
movements.add(n2.getAngle());
}
};
}
}
// If I have stored any angles:
if(movements.size() > 0) {
// Reset my position to where it was before moving
setPosition(lastPosition.clone());
// Calculate the sum of angles
double totalAngle = 0;
for (double angle : movements) {
totalAngle += angle;
}
// Create a vector with total angle and step size.
Vector v = Vector.createFromLengthAndAngle(stepSize, totalAngle);
// Move in the direction of calculated vector
Point newPos = getPosition().clone();
newPos.moveByVector(v);
setRotation(totalAngle);
getPosition().moveByVector(v);
}
return isClose;
}
Simply I calculate two normal vectors (perpendicular to the vector) pointing from me to colliding element. Then I store angle and after all I create new vector with total of angles.
It works on collision between 2 elements. However when there are multiple elements it starts jiggling (bouncing) and sometimes it even breaks the collision constraints (rules) and walks into a tree.
What am I doing wrong here?
EDIT btw my colliders are all circles
this(GameBodyObject) to the position where the user clicked, like in Dota 2 or Warcraft 3 \$\endgroup\$Gameobject \$\endgroup\$