Skip to content

Commit

Permalink
changed collider for soliders boid agents to circle
Browse files Browse the repository at this point in the history
  • Loading branch information
keshav2010 committed Apr 8, 2024
1 parent 31856f6 commit 62e823d
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 121 deletions.
20 changes: 14 additions & 6 deletions gameserver/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,20 @@ export enum CoreErrorCodes {

export const MOVABLE_UNIT_CONSTANTS = {
MAX_STEER_FORCE: 10,
MAX_REPEL_FORCE: 70,
MAX_REPEL_FORCE: 30,

DESIRED_DIST_FROM_TARGET: 20,
ACCEPTABLE_DIST_FROM_EXPECTED_POS: 1,
MAX_DISTANCE_OFFSET_ALLOWED_FROM_EXPECTED_POSITION: 50,

/**
* NEARBY_SEARCH_RADI serves following purpose
* 1. detect nearby allies under attack
* 2. find attack target
*/
NEARBY_SEARCH_RADI: 150,
ENEMY_SEARCH_RADIUS: 200,
DESIRED_SEPERATION_DIST: 55, //to initiate repulsion force
MAX_TARGETPOS_OVERLAP_DIST: 50,

/**
*
*/
MINIMUM_SEPERATION_DISTANCE_BETWEEN_UNITS: 30, //to initiate repulsion force
MAX_TARGETPOS_OVERLAP_DIST: 70,
};
46 changes: 25 additions & 21 deletions gameserver/core/Scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class Scene extends Quadtree<TypeQuadtreeItem> {
}

/**
* Gets units which are within the bounding box(square)
* Gets units which are within the region
* @param {*} soldier
* @param {*} searchRadius
* @returns
Expand All @@ -49,34 +49,38 @@ export class Scene extends Quadtree<TypeQuadtreeItem> {
y: number,
searchRadius: number,
type?: SceneObjectType[]
) {
let result = this.colliding({ x, y }, (a, b) => {
// a=> 1st arg, b => actual quadtree object
const aPos = new SAT.Vector(a.x, a.y);
const bPos = new SAT.Vector(b.x + b.width! / 2, b.y + b.height! / 2);
let distance = aPos.clone().sub(bPos).len();
return distance <= 2 * searchRadius;
) {
let result = this.colliding({x, y}, (a, b) => {
// Create circles for each object
const aCircle = new SAT.Circle(
new SAT.Vector(a.x, a.y),
Math.max(searchRadius, a.r || 0),
);
const bCircle = new SAT.Circle(new SAT.Vector(b.x, b.y), b.r);

// Perform circle-circle collision detection
const response = new SAT.Response();
const collided = SAT.testCircleCircle(aCircle, bCircle, response);
return collided;
});

if (type) result = result.filter((body) => type?.includes(body.type));
return result;
}
}

//Check if unit/sceneItem is colliding with other units/soldiers
checkCollisionOnObject(
sceneItem: ISceneItem,
callback: (arg0: SAT.Response, arg1: ISceneItem[]) => void
) {
const mainCollidingObject = sceneItem.getSceneItem();
//fetch all bodies which are colliding with the soldier specified by x,y,w,h in arg.
let collidingBodies = this.colliding({
x: mainCollidingObject.pos.x,
y: mainCollidingObject.pos.y,
width: mainCollidingObject.w,
height: mainCollidingObject.h,
});

//fetch all bodies which are colliding with the soldier specified by x,y,r in arg.
let collidingBodies = this.getNearbyUnits(
mainCollidingObject.pos.x,
mainCollidingObject.pos.y,
mainCollidingObject.r
);
//Colliding Bodies will always have 1 element, which is the soldier itself.
if (collidingBodies.length < 2) return;

//Obtain "SAT.Response" for each collision.
const satBoxPolygons = collidingBodies
Expand All @@ -92,9 +96,9 @@ export class Scene extends Quadtree<TypeQuadtreeItem> {
}

const res = new SAT.Response();
const isColliding = SAT.testPolygonPolygon(
mainCollidingObject.toPolygon(),
collidingBody.getSceneItem().toPolygon(),
const isColliding = SAT.testCircleCircle(
mainCollidingObject,
collidingBody.getSceneItem(),
res
);
if (!isColliding) return;
Expand Down
14 changes: 5 additions & 9 deletions gameserver/core/types/SceneObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,26 @@ import SAT from "sat";
* @classdesc Any object that is meant to be part of the Scene should extend this class.
*/
export type SceneObjectType = "FIXED" | "MOVABLE";
export class SceneObject extends SAT.Box {
export class SceneObject extends SAT.Circle {
id: string;
x: number;
y: number;
width: number;
height: number;
r: number;
type: SceneObjectType;
collidable: boolean;
constructor(
id: string,
x: number,
y: number,
width = 35,
height = 35,
radius = 35,
type: SceneObjectType,
collidable: boolean = true
) {
// {pos:{x,y}}
super(new SAT.Vector(x, y), width, height);
super(new SAT.Vector(x, y), radius);
this.id = id;
this.x = this.pos.x;
this.y = this.pos.y;
this.width = this.w;
this.height = this.h;
this.r = radius;
this.type = type;
this.collidable = collidable;
}
Expand Down
3 changes: 1 addition & 2 deletions gameserver/core/types/TypeQuadtreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { SceneObjectType } from "./SceneObject";
export type TypeQuadtreeItem = {
x: Quadtree.QuadtreeItem["x"];
y: Quadtree.QuadtreeItem["y"];
width?: Quadtree.QuadtreeItem["width"];
height?: Quadtree.QuadtreeItem["height"];
r?: number;
id: string;
type: SceneObjectType;
collidable: boolean;
Expand Down
1 change: 0 additions & 1 deletion gameserver/schema/PlayerState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export class PlayerState extends Schema implements ISceneItem {
x,
y,
100,
100,
"FIXED",
false
);
Expand Down
77 changes: 36 additions & 41 deletions gameserver/schema/SoldierState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { MOVABLE_UNIT_CONSTANTS } from "../config";
import { GameStateManagerType } from "./PlayerState";
import { ISceneItem } from "../core/types/ISceneItem";
import { TypeQuadtreeItem } from "../core/types/TypeQuadtreeItem";
import { IBoidAgent } from '../core/types/IBoidAgent';
import { IBoidAgent } from "../core/types/IBoidAgent";

function mapRange(
val: number,
Expand All @@ -26,10 +26,7 @@ function mapRange(
return targetRangeStart + normalizedGivenRange * targetRange;
}

export class SoldierState
extends Schema
implements ISceneItem, IBoidAgent
{
export class SoldierState extends Schema implements ISceneItem, IBoidAgent {
@type("number") currentPositionX: number = 0;
@type("number") currentPositionY: number = 0;

Expand All @@ -43,8 +40,7 @@ export class SoldierState

@type("string") type: SoldierType = "SPEARMAN";

@type("number") width: number = 32;
@type("number") height: number = 32;
@type("number") radius: number = 32;

@type("number") health: number = 100;
@type("number") speed: number;
Expand Down Expand Up @@ -104,7 +100,7 @@ export class SoldierState
this.damage = SoldierTypeConfig[this.type].damage;
this.cost = SoldierTypeConfig[this.type].cost;

this.sceneItemRef = new SceneObject(this.id, x, y, 32, 32, "MOVABLE", true);
this.sceneItemRef = new SceneObject(this.id, x, y, 32, "MOVABLE", true);
}

setGroupLeaderId(leaderId: string) {
Expand All @@ -119,7 +115,7 @@ export class SoldierState
}

getExpectedPosition() {
return new SAT.Vector(this.expectedPositionX + this.offsetFromPosition.x, this.expectedPositionY + this.offsetFromPosition.y);
return new SAT.Vector(this.expectedPositionX, this.expectedPositionY);
}

setAttackTarget(target: SoldierState | null) {
Expand Down Expand Up @@ -149,7 +145,7 @@ export class SoldierState
let distanceToExpectedPos = expectedPos.sub(this.getSceneItem().pos).len();
if (
distanceToExpectedPos <=
MOVABLE_UNIT_CONSTANTS.ACCEPTABLE_DIST_FROM_EXPECTED_POS
MOVABLE_UNIT_CONSTANTS.MAX_DISTANCE_OFFSET_ALLOWED_FROM_EXPECTED_POSITION
) {
this.isAtDestination = true;
} else this.isAtDestination = false;
Expand All @@ -159,7 +155,8 @@ export class SoldierState
hasReachedDestination() {
let distanceToExpectedPos = this.getDistanceFromExpectedPosition();
this.isAtDestination =
distanceToExpectedPos <= MOVABLE_UNIT_CONSTANTS.DESIRED_DIST_FROM_TARGET;
distanceToExpectedPos <=
MOVABLE_UNIT_CONSTANTS.MAX_DISTANCE_OFFSET_ALLOWED_FROM_EXPECTED_POSITION;
return this.isAtDestination;
}

Expand Down Expand Up @@ -192,7 +189,7 @@ export class SoldierState

if (
distanceFromExpectedPosition <=
MOVABLE_UNIT_CONSTANTS.DESIRED_DIST_FROM_TARGET
MOVABLE_UNIT_CONSTANTS.MAX_DISTANCE_OFFSET_ALLOWED_FROM_EXPECTED_POSITION
) {
desiredVector.scale(0);
} else desiredVector.normalize().scale(this.speed);
Expand All @@ -210,8 +207,8 @@ export class SoldierState
) {
const soldier = this.getSceneItem();
const nearbyUnits = stateManager.scene.getNearbyUnits(
soldier.x + soldier.w / 2,
soldier.y + soldier.h / 2,
soldier.x + soldier.r / 2,
soldier.y + soldier.r / 2,
MOVABLE_UNIT_CONSTANTS.NEARBY_SEARCH_RADI,
["MOVABLE"]
);
Expand Down Expand Up @@ -247,7 +244,8 @@ export class SoldierState
.len();

const unitSeperationBetweenCertainThreshold =
distanceBetweenUnits <= MOVABLE_UNIT_CONSTANTS.DESIRED_SEPERATION_DIST;
distanceBetweenUnits <=
MOVABLE_UNIT_CONSTANTS.MINIMUM_SEPERATION_DISTANCE_BETWEEN_UNITS;

if (!unitSeperationBetweenCertainThreshold) return;

Expand Down Expand Up @@ -296,34 +294,14 @@ export class SoldierState

tick(delta: number, stateManager: GameStateManagerType) {
this.currentState = this.stateMachine.currentState as any;
const soldier = this.getSceneItem();

// if group-leader not present anymore, ensure offset is 0
if (
!stateManager.getPlayer(this.playerId)?.getSoldier(this.groupLeaderId)
) {
// in case group-leader instance not found, we reset offset
const groupLeaderRef = stateManager
.getPlayer(this.playerId)
?.getSoldier(this.groupLeaderId);
if (!groupLeaderRef) {
this.groupLeaderId = null;
this.offsetFromPosition = new SAT.Vector(0, 0);
}

const frictionForce = this.velocityVector.clone().scale(-1.0 * delta);
this.velocityVector.add(frictionForce);
const steerForce = this.getSteerVector(this.getExpectedPosition());
let seperationForce = this.getSeperationVector(
stateManager,
(a: SoldierState, b: SoldierState) => {
return a.hasReachedDestination() && b.hasReachedDestination();
}
);

const netForce = steerForce.clone().add(seperationForce);
this.applyForce(netForce);

this.velocityVector.normalize().scale(this.speed * delta);
const newPosition = soldier.pos.clone().add(this.getVelocityVector());

this.setPosition(newPosition);

}
stateManager.scene.checkCollisionOnObject(
this,
(
Expand Down Expand Up @@ -370,4 +348,21 @@ export class SoldierState

this.stateMachine.tick({ delta, stateManager, soldier: this });
}

move(delta: number, stateManager: GameStateManagerType) {
const steerForce = this.getSteerVector(this.getExpectedPosition());
const seperationForce = this.getSeperationVector(
stateManager,
(a: SoldierState, b: SoldierState) => {
return a.hasReachedDestination() && b.hasReachedDestination();
}
);
const netForce = steerForce.clone().add(seperationForce);
this.applyForce(netForce);
this.velocityVector.normalize().scale(this.speed * delta);
const newPosition = this.getSceneItem()!
.pos.clone()
.add(this.getVelocityVector());
this.setPosition(newPosition);
}
}
Loading

0 comments on commit 62e823d

Please sign in to comment.