From 8d709709a8f3f0a9c0a315c630c7b737361840d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Wed, 7 Apr 2021 09:25:40 +0200 Subject: [PATCH] Improve FollowEntityAI to recompute path only on demand --- .../base/lib/ai/FollowEntityAI.java | 84 +++++++++++++------ .../path/{NPCPath.java => EntityPath.java} | 18 ++-- .../base/util/path/MovementPath.java | 23 +++++ .../base/util/path/TurnSegment.java | 8 +- 4 files changed, 94 insertions(+), 39 deletions(-) rename api/src/main/java/com/bartlomiejpluta/base/util/path/{NPCPath.java => EntityPath.java} (59%) diff --git a/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java b/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java index 024d0787..0a04e552 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java +++ b/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java @@ -4,47 +4,79 @@ import com.bartlomiejpluta.base.api.ai.AI; import com.bartlomiejpluta.base.api.ai.NPC; import com.bartlomiejpluta.base.api.entity.Entity; import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; -import com.bartlomiejpluta.base.api.move.Direction; +import com.bartlomiejpluta.base.api.move.MoveEvent; +import com.bartlomiejpluta.base.util.path.MovementPath; +import com.bartlomiejpluta.base.util.path.PathExecutor; import com.bartlomiejpluta.base.util.pathfinder.PathFinder; -import org.joml.Vector2i; +import lombok.NonNull; +import lombok.Setter; -public class FollowEntityAI implements AI { - private final NPC npc; - private final Entity target; - private final PathFinder pathFinder; - private final int range; +public abstract class FollowEntityAI implements AI { - private final float recalculateInterval; - private float accumulator = 0.0f; + private final PathFinder finder; + private final PathExecutor executor; + private final N npc; + private final T target; - public FollowEntityAI(PathFinder pathFinder, float recalculateInterval, NPC npc, Entity target, int range) { - this.pathFinder = pathFinder; - this.recalculateInterval = recalculateInterval; + @Setter + private AI idleAI; + + @Setter + private int range = 20; + + private MovementPath path = null; + + protected FollowEntityAI(@NonNull PathFinder finder, @NonNull N npc, @NonNull T target) { + this.finder = finder; this.npc = npc; - this.range = range; this.target = target; + this.executor = new PathExecutor<>(npc); + } + + public void recomputePath() { + path = null; + } + + public void recomputePath(@NonNull MoveEvent event) { + var movable = event.getMovable(); + + // Refresh only when target has been displaced + // or another entity is blocking current path + if (movable == target || (path != null && path.contains(movable))) { + path = null; + } } @Override public void nextActivity(ObjectLayer layer, float dt) { - var distance = npc.manhattanDistance(target); + if (!npc.isMoving()) { + var distance = npc.manhattanDistance(target); - if (!npc.isMoving() && 1 < distance && distance < range && accumulator >= recalculateInterval) { - var path = pathFinder.findSequence(layer, npc.getCoordinates(), target.getCoordinates()); + if (distance == 1) { + npc.setFaceDirection(npc.getDirectionTowards(target)); + interact(npc, target); + } else if (distance < range) { + follow(npc, target); - if (!path.isEmpty()) { - accumulator = recalculateInterval; + if (path == null) { + path = finder.findPath(layer, npc, target.getCoordinates()); + executor.setPath(path); + } - var node = path.getFirst().sub(npc.getCoordinates(), new Vector2i()); - var direction = Direction.ofVector(node); - var movement = npc.prepareMovement(direction); - layer.pushMovement(movement); + executor.execute(layer, dt); } else { - accumulator = 0.0f; + idle(npc, target); + + if (idleAI != null) { + idleAI.nextActivity(layer, dt); + } } - } - - accumulator += dt; } + + protected abstract void interact(N npc, T target); + + protected abstract void follow(N npc, T target); + + protected abstract void idle(N npc, T target); } diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java b/api/src/main/java/com/bartlomiejpluta/base/util/path/EntityPath.java similarity index 59% rename from api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java rename to api/src/main/java/com/bartlomiejpluta/base/util/path/EntityPath.java index 2a7560f0..7e4dbf76 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/path/EntityPath.java @@ -1,48 +1,48 @@ package com.bartlomiejpluta.base.util.path; -import com.bartlomiejpluta.base.api.ai.NPC; +import com.bartlomiejpluta.base.api.entity.Entity; import com.bartlomiejpluta.base.api.move.Direction; import lombok.Getter; import java.util.ArrayList; import java.util.List; -public class NPCPath implements Path { +public class EntityPath implements Path { @Getter private final List> path = new ArrayList<>(); - public NPCPath add(PathSegment segment) { + public EntityPath add(PathSegment segment) { path.add(segment); return this; } - public NPCPath addFirst(PathSegment segment) { + public EntityPath addFirst(PathSegment segment) { path.add(0, segment); return this; } - public NPCPath move(Direction direction) { + public EntityPath move(Direction direction) { path.add(new MoveSegment<>(direction)); return this; } - public NPCPath move(Direction direction, boolean ignore) { + public EntityPath move(Direction direction, boolean ignore) { path.add(new MoveSegment<>(direction, ignore)); return this; } - public NPCPath turn(Direction direction) { + public EntityPath turn(Direction direction) { path.add(new TurnSegment<>(direction)); return this; } - public NPCPath wait(float seconds) { + public EntityPath wait(float seconds) { path.add(new WaitSegment<>(seconds)); return this; } - public NPCPath run(Runnable runnable) { + public EntityPath run(Runnable runnable) { path.add(new RunSegment<>(runnable)); return this; } diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/path/MovementPath.java b/api/src/main/java/com/bartlomiejpluta/base/util/path/MovementPath.java index 35d210e2..86ffb42d 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/path/MovementPath.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/path/MovementPath.java @@ -2,6 +2,7 @@ package com.bartlomiejpluta.base.util.path; import com.bartlomiejpluta.base.api.move.Direction; import com.bartlomiejpluta.base.api.move.Movable; +import org.joml.Vector2ic; import java.util.ArrayList; import java.util.List; @@ -24,6 +25,28 @@ public class MovementPath implements Path { return this; } + public boolean contains(Movable movable) { + var coordinates = movable.getCoordinates(); + + for (var segment : path) { + if (segment.x == coordinates.x() && segment.y == coordinates.y()) { + return true; + } + } + + return false; + } + + public boolean contains(Vector2ic vector) { + for (var segment : path) { + if (segment.x == vector.x() && segment.y == vector.y()) { + return true; + } + } + + return false; + } + public boolean contains(int x, int y) { for (var segment : path) { if (segment.x == x && segment.y == y) { diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/path/TurnSegment.java b/api/src/main/java/com/bartlomiejpluta/base/util/path/TurnSegment.java index 0a4398eb..8342050f 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/path/TurnSegment.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/path/TurnSegment.java @@ -1,12 +1,12 @@ package com.bartlomiejpluta.base.util.path; -import com.bartlomiejpluta.base.api.ai.NPC; +import com.bartlomiejpluta.base.api.entity.Entity; import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; import com.bartlomiejpluta.base.api.move.Direction; import static java.util.Objects.requireNonNull; -public class TurnSegment implements PathSegment { +public class TurnSegment implements PathSegment { private final Direction direction; public TurnSegment(Direction direction) { @@ -14,8 +14,8 @@ public class TurnSegment implements PathSegment { } @Override - public PathProgress perform(T npc, ObjectLayer layer, float dt) { - npc.setFaceDirection(direction); + public PathProgress perform(T entity, ObjectLayer layer, float dt) { + entity.setFaceDirection(direction); return PathProgress.SEGMENT_DONE; } }