package com.gebauz.burutaru.game.entities;
import java.util.Vector;
import com.gebauz.bauzoid.graphics.sprite.SpriteTransform;
import com.gebauz.bauzoid.math.Line2;
import com.gebauz.bauzoid.math.Vector2;
import com.gebauz.bauzoid.math.collision.AABoundingBox;
import com.gebauz.bauzoid.math.collision.BaseShapeElement;
import com.gebauz.bauzoid.math.collision.CollisionResult;
import com.gebauz.bauzoid.math.collision.Shape;
import com.gebauz.burutaru.game.GameLogic;
import com.gebauz.burutaru.game.entities.enemies.BaseEnemy;
import com.gebauz.burutaru.game.entities.level.elements.BaseLevelElement;
/** Global collision world that contains different colliders. */
public class CollisionWorld
extends Entity
{
// Constants========================================================================================
public static final int FILTER_LEVEL_ELEMENTS =
1;
public static final int FILTER_ENEMIES =
2;
public static final int FILTER_ALL = FILTER_LEVEL_ELEMENTS | FILTER_ENEMIES
;
// Embedded Types===================================================================================
public static class CollisionInfo
extends CollisionResult
{
public BaseLevelElement collidingLevelObject =
null;
//public Entity collidingEntity = null;
public BaseEnemy.
Instance collidingEnemyInstance =
null;
}
public class PassiveColliderInfo
{
public BaseLevelElement levelObject =
null;
public Shape shape =
null;
public PassiveColliderInfo
(BaseLevelElement obj,
Shape objShape
)
{
levelObject = obj
;
shape = objShape
;
}
}
public class EnemyColliderInfo
{
public BaseEnemy.
Instance enemyInstance =
null;
public Shape shape =
null;
public EnemyColliderInfo
(BaseEnemy.
Instance obj,
Shape objShape
)
{
enemyInstance = obj
;
shape = objShape
;
}
}
public class ColliderInfo
{
public int collisionFilter =
0;
public BaseEnemy.
Instance enemyInstance =
null;
public BaseLevelElement levelElement =
null;
public Shape shape =
null;
public ColliderInfo
(BaseEnemy.
Instance enemy,
Shape enemyShape
)
{
collisionFilter = FILTER_ENEMIES
;
enemyInstance = enemy
;
shape = enemyShape
;
}
public ColliderInfo
(BaseLevelElement levelObject,
Shape objShape
)
{
collisionFilter = FILTER_LEVEL_ELEMENTS
;
levelElement = levelObject
;
shape = objShape
;
}
public final boolean contains
(int filter
)
{
return ((collisionFilter
& filter
) !=
0);
}
public final boolean isColliderAlive
()
{
if (contains
(FILTER_LEVEL_ELEMENTS
))
return (levelElement.
isAlive());
if (contains
(FILTER_ENEMIES
))
return (enemyInstance.
isAlive());
return false;
}
}
// Fields===========================================================================================
/** Collection of passive colliders. */
private Vector<PassiveColliderInfo
> mPassiveColliders =
new Vector<PassiveColliderInfo
>();
private Vector<EnemyColliderInfo
> mEnemyColliders =
new Vector<EnemyColliderInfo
>();
private Vector<ColliderInfo
> mColliders =
new Vector<ColliderInfo
>();
// Methods==========================================================================================
public CollisionWorld
(GameLogic gameLogic
)
{
super(gameLogic
);
}
@
Override
public void initAsync
()
{
}
@
Override
public void init
()
{
}
@
Override
public void exit
()
{
}
@
Override
public void update
(float deltaTime
)
{
}
@
Override
public void render
()
{
}
/** Add Reference to a passive collider. */
public void addPassiveCollider
(BaseLevelElement obj,
Shape shape
)
{
mPassiveColliders.
add(new PassiveColliderInfo
(obj, shape
));
mColliders.
add(new ColliderInfo
(obj, shape
));
}
public void addEnemyCollider
(BaseEnemy.
Instance obj,
Shape shape
)
{
mEnemyColliders.
add(new EnemyColliderInfo
(obj, shape
));
mColliders.
add(new ColliderInfo
(obj, shape
));
}
/** Check for collision of shape with passive colliders, return contact point and minimal translation vector.
* Suitable for ship<->environment collisions.
*/
public CollisionInfo collideWithResponsePassiveColliders
(int filter,
Shape shape, Vector2 move
)
{
CollisionInfo result =
new CollisionInfo
();
Vector2 resultPenetration =
new Vector2
();
Vector2 resultContactPoint =
new Vector2
();
// Check broadphase collision
shape.
transform.
x += move.
x;
shape.
transform.
y += move.
y;
AABoundingBox box1 = shape.
getTransformedBoundingBox();
// Check collision between object and level objects
for (int i =
0; i
< mColliders.
size(); i++
)
{
ColliderInfo info = mColliders.
get(i
);
if (!info.
contains(filter
))
continue;
/*if (!info.levelObject.isAlive())
continue;*/
if (!info.
isColliderAlive())
continue;
AABoundingBox box2 = info.
shape.
getTransformedBoundingBox();
if (!box1.
intersetcs(box2
))
continue;
// perform collision detection between actor's convex hull and shape
//CollisionResult cr = shape.getConvexHull().collide(info.collider.getShape(), move);
//CollisionResult cr = shape.getShapeElement(0).collide(info.shape, move);
//if (cr.isColliding)
if (shape.
getShapeElement(0).
collide(info.
shape, move, resultPenetration, resultContactPoint
))
{
result.
isColliding =
true;
result.
penetrationVector = resultPenetration
;
if (info.
contains(FILTER_LEVEL_ELEMENTS
))
result.
collidingLevelObject = info.
levelElement;
else if (info.
contains(FILTER_ENEMIES
))
result.
collidingEnemyInstance = info.
enemyInstance;
if (resultContactPoint
!=
null)
result.
contactPoint = resultContactPoint
;
}
}
shape.
transform.
x -= move.
x;
shape.
transform.
y -= move.
y;
return result
;
}
/* public CollisionInfo collideEnemyWithResponse(Shape shape, Vector2 move)
{
CollisionInfo result = new CollisionInfo();
// Check broadphase collision
shape.transform.x += move.x;
shape.transform.y += move.y;
AABoundingBox box1 = shape.getTransformedBoundingBox();
shape.transform.x -= move.x;
shape.transform.y -= move.y;
Vector2 resultPenetration = new Vector2();
Vector2 resultContactPoint = new Vector2();
// Check collision between object and level objects
for (int i = 0; i < mEnemyColliders.size(); i++)
{
EnemyColliderInfo info = mEnemyColliders.get(i);
if (!info.enemyInstance.isAlive())
continue;
AABoundingBox box2 = info.enemyInstance.getShape().getTransformedBoundingBox();
if (!box1.intersetcs(box2))
continue;
// perform collision detection between actor's convex hull and shape
//CollisionResult cr = shape.getConvexHull().collide(info.collider.getShape(), move);
//CollisionResult cr = shape.getShapeElement(0).collide(info.enemyInstance.getShape(), move);
//if (cr.isColliding)
if (shape.getShapeElement(0).collide(info.enemyInstance.getShape(), move, resultPenetration, resultContactPoint))
{
result.isColliding = true;
result.penetrationVector = resultPenetration;
result.collidingEnemyInstance = info.enemyInstance;
if (resultContactPoint != null)
result.contactPoint = resultContactPoint;
}
}
return result;
}*/
/** Check for collision by raycasting and return a contact point only (no minimal translation vector).
* Only suitable for thin objects where raycasting suffices as a collision detection scheme.
* Suitable for fast, thin projectile <-> environment collisions.
*/
public CollisionInfo collideRaycast
(Vector2 pos, Vector2 velocity
)
{
CollisionInfo result =
new CollisionInfo
();
// Check broadphase collision
AABoundingBox box1 = AABoundingBox.
fromRay(pos, velocity
);
float minimumDistanceSqr =
Float.
MAX_VALUE;
// Check raycasts between object and level objects
for (int i =
0; i
< mPassiveColliders.
size(); i++
)
{
PassiveColliderInfo info = mPassiveColliders.
get(i
);
if (!info.
levelObject.
isAlive())
continue;
AABoundingBox box2 = info.
shape.
getTransformedBoundingBox();
if (!box1.
intersetcs(box2
))
continue;
// transform ray into the shape's space
SpriteTransform t = info.
shape.
transform;
Vector2 p1 = t.
worldToSprite(pos.
x, pos.
y);
Vector2 p2 = t.
worldToSprite(pos.
x + velocity.
x, pos.
y + velocity.
y);
Line2 ray =
new Line2
(p1, p2
);
for (int j =
0; j
< info.
shape.
getShapeElementCount(); j++
)
{
BaseShapeElement e = info.
shape.
getShapeElement(j
);
CollisionResult cr = e.
collideRayCast(ray
);
if (cr.
isColliding)
{
Vector2 posToContact =
new Vector2
(cr.
contactPoint.
x - p1.
x, cr.
contactPoint.
y - p1.
y);
float distanceSqr = posToContact.
squaredLength();
if (distanceSqr
< minimumDistanceSqr
)
{
result.
isColliding =
true;
minimumDistanceSqr = distanceSqr
;
result.
contactPoint = t.
spriteToWorld(cr.
contactPoint.
x, cr.
contactPoint.
y);
}
}
}
}
return result
;
}
public CollisionInfo collideEnemyRaycast
(Vector2 pos, Vector2 velocity
)
{
CollisionInfo result =
new CollisionInfo
();
// Check broadphase collision
AABoundingBox box1 = AABoundingBox.
fromRay(pos, velocity
);
//Line2 ray = new Line2(pos, velocity);
float minimumDistanceSqr =
Float.
MAX_VALUE;
// Check raycasts between object and level objects
for (int i =
0; i
< mEnemyColliders.
size(); i++
)
{
EnemyColliderInfo info = mEnemyColliders.
get(i
);
if (!info.
enemyInstance.
isAlive())
continue;
AABoundingBox box2 = info.
enemyInstance.
getShape().
getTransformedBoundingBox();
if (!box1.
intersetcs(box2
))
continue;
// transform ray into the shape's space
SpriteTransform t = info.
enemyInstance.
getShape().
transform;
Vector2 p1 = t.
worldToSprite(pos.
x, pos.
y);
Vector2 p2 = t.
worldToSprite(pos.
x + velocity.
x, pos.
y + velocity.
y);
Line2 ray =
new Line2
(p1, p2
);
for (int j =
0; j
< info.
enemyInstance.
getShape().
getShapeElementCount(); j++
)
{
BaseShapeElement e = info.
enemyInstance.
getShape().
getShapeElement(j
);
CollisionResult cr = e.
collideRayCast(ray
);
if (cr.
isColliding)
{
Vector2 posToContact =
new Vector2
(cr.
contactPoint.
x - p1.
x, cr.
contactPoint.
y - p1.
y);
float distanceSqr = posToContact.
squaredLength();
if (distanceSqr
< minimumDistanceSqr
)
{
result.
isColliding =
true;
minimumDistanceSqr = distanceSqr
;
result.
contactPoint = t.
spriteToWorld(cr.
contactPoint.
x, cr.
contactPoint.
y);
}
}
}
}
return result
;
}
// Getters/Setters==================================================================================
}