multiple hitboxes per bone + data fixes

This commit is contained in:
austin 2024-08-13 14:09:18 -04:00
parent 06b068643e
commit 0b2086a1d4
4 changed files with 202 additions and 79 deletions

View File

@ -15,22 +15,32 @@
}, },
{ {
"type": "hurt", "type": "hurt",
"bone": "Leg.L", "bone": "Forearm.L",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Leg.R",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Shoulder.L",
"radius": 0.06 "radius": 0.06
}, },
{ {
"type": "hurt", "type": "hurt",
"bone": "Shoulder.R", "bone": "Forearm.R",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Leg.L",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Leg.R",
"radius": 0.06
},
{
"type": "hurt",
"bone": "LowerLeg.L",
"radius": 0.06
},
{
"type": "hurt",
"bone": "LowerLeg.R",
"radius": 0.06 "radius": 0.06
}, },
{ {
@ -38,16 +48,11 @@
"bone": "Neck", "bone": "Neck",
"radius": 0.06 "radius": 0.06
}, },
{
"type": "hurt",
"bone": "Bone",
"radius": 0.08
},
{ {
"type": "hurt", "type": "hurt",
"bone": "Head", "bone": "Head",
"radius": 0.06, "radius": 0.07,
"offset": [0.0,3.0,0.0] "offset": [0.0,0.13,0.0]
} }
], ],
"tokens" : [ "tokens" : [

View File

@ -69,6 +69,11 @@
"bone": "Blade2", "bone": "Blade2",
"radius": 0.04 "radius": 0.04
}, },
{
"type": "hit_connected",
"bone": "Blade3",
"radius": 0.04
},
{ {
"type": "hit_connected", "type": "hit_connected",
"bone": "Blade3", "bone": "Blade3",

View File

@ -551,6 +551,7 @@ Movement tweaks
(08/13/2024) (08/13/2024)
Hitbox support offsets now Hitbox support offsets now
Multiple hitboxes per bone
# TODO # TODO

View File

@ -1,10 +1,10 @@
package electrosphere.entity.state.hitbox; package electrosphere.entity.state.hitbox;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
@ -64,8 +64,10 @@ public class HitboxCollectionState {
//the list of all geoms in the collection state //the list of all geoms in the collection state
List<DGeom> geoms = new LinkedList<DGeom>(); List<DGeom> geoms = new LinkedList<DGeom>();
//the map of bone -> hitbox shape in ode4j //the map of bone -> list of states of individual shapes attached to that bone
Map<String,DGeom> hitboxGeomMap = new HashMap<String,DGeom>(); Map<String,List<HitboxState>> boneHitboxMap = new HashMap<String,List<HitboxState>>();
//map of hitbox state -> geometry
Map<HitboxState,DGeom> stateGeomMap = new HashMap<HitboxState,DGeom>();
//the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision //the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision
Map<DGeom,HitboxState> geomStateMap = new HashMap<DGeom,HitboxState>(); Map<DGeom,HitboxState> geomStateMap = new HashMap<DGeom,HitboxState>();
@ -154,11 +156,16 @@ public class HitboxCollectionState {
} break; } break;
} }
//create the state for the individual shape
HitboxState state = new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false);
//add to structures
if(hitboxDataRaw.getBone() != null){ if(hitboxDataRaw.getBone() != null){
rVal.hitboxGeomMap.put(hitboxDataRaw.getBone(),geom); rVal.addHitbox(hitboxDataRaw.getBone(),state);
} else {
LoggerInterface.loggerEngine.WARNING("Trying to attach hitbox to bone where bone cannot be found: " + hitboxDataRaw.getBone());
} }
rVal.geoms.add(geom); rVal.registerGeom(geom,state);
rVal.geomStateMap.put(geom,new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false));
} }
//create body with all the shapes //create body with all the shapes
@ -189,28 +196,34 @@ public class HitboxCollectionState {
* @return The hitbox state that has been attached to the entity * @return The hitbox state that has been attached to the entity
*/ */
public static HitboxCollectionState attachHitboxStateWithCallback(HitboxManager manager, CollisionEngine collisionEngine, Entity entity, HitboxData data, HitboxPositionCallback callback){ public static HitboxCollectionState attachHitboxStateWithCallback(HitboxManager manager, CollisionEngine collisionEngine, Entity entity, HitboxData data, HitboxPositionCallback callback){
HitboxCollectionState rVal = new HitboxCollectionState(); throw new UnsupportedOperationException("Not yet implemented!");
// HitboxCollectionState rVal = new HitboxCollectionState();
//create the shapes // //create the shapes
rVal.hitboxGeomMap.put(data.getBone(),CollisionBodyCreation.createShapeSphere(collisionEngine, data.getRadius(), Collidable.TYPE_OBJECT_BIT)); // DGeom geom = CollisionBodyCreation.createShapeSphere(collisionEngine, data.getRadius(), Collidable.TYPE_OBJECT_BIT);
//create body with all the shapes // //create the state for the individual shape
DGeom[] geomArray = rVal.hitboxGeomMap.values().toArray(new DGeom[rVal.hitboxGeomMap.values().size()]); // HitboxState state = new HitboxState(data.getBone(), data, getType(data), getSubType(data), getShapeType(data), false);
rVal.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray); // rVal.addHitbox(data.getBone(), state);
// rVal.registerGeom(geom,state);
//register collidable with collision engine // //create body with all the shapes
Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); // DGeom[] geomArray = rVal.geoms.toArray(new DGeom[rVal.geoms.size()]);
collisionEngine.registerCollisionObject(rVal.body, collidable); // rVal.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray);
//attach // //register collidable with collision engine
entity.putData(EntityDataStrings.HITBOX_DATA, rVal); // Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT);
rVal.parent = entity; // collisionEngine.registerCollisionObject(rVal.body, collidable);
//register // //attach
manager.registerHitbox(rVal); // entity.putData(EntityDataStrings.HITBOX_DATA, rVal);
rVal.manager = manager; // rVal.parent = entity;
return rVal; // //register
// manager.registerHitbox(rVal);
// rVal.manager = manager;
// return rVal;
} }
/** /**
@ -228,22 +241,24 @@ public class HitboxCollectionState {
*/ */
public void updateHitboxPositions(CollisionEngine collisionEngine){ public void updateHitboxPositions(CollisionEngine collisionEngine){
if(parent != null && !isServer && EntityUtils.getActor(parent) != null){ if(parent != null && !isServer && EntityUtils.getActor(parent) != null){
if(!this.hitboxGeomMap.isEmpty()){ if(!this.geoms.isEmpty()){
Vector3d entityPosition = EntityUtils.getPosition(parent); Vector3d entityPosition = EntityUtils.getPosition(parent);
this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition));
for(String boneName : this.hitboxGeomMap.keySet()){ for(String boneName : this.boneHitboxMap.keySet()){
Vector3f bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName); Vector3f bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName);
DGeom geom = this.hitboxGeomMap.get(boneName); for(HitboxState state : this.boneHitboxMap.get(boneName)){
HitboxState shapeStatus = this.geomStateMap.get(geom); DGeom geom = this.stateGeomMap.get(state);
switch(shapeStatus.shapeType){ HitboxState shapeStatus = this.geomStateMap.get(geom);
case SPHERE: { switch(shapeStatus.shapeType){
this.updateSphereShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); case SPHERE: {
} break; this.updateSphereShapePosition(collisionEngine,boneName,shapeStatus,bonePosition);
case CAPSULE: { } break;
this.updateCapsuleShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); case CAPSULE: {
} break; this.updateCapsuleShapePosition(collisionEngine,boneName,shapeStatus,bonePosition);
case STATIC_CAPSULE: { } break;
} break; case STATIC_CAPSULE: {
} break;
}
} }
} }
} else if(positionCallback != null){ } else if(positionCallback != null){
@ -254,22 +269,24 @@ public class HitboxCollectionState {
PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation);
} }
} else if(parent != null && isServer && EntityUtils.getPoseActor(parent) != null){ } else if(parent != null && isServer && EntityUtils.getPoseActor(parent) != null){
if(!this.hitboxGeomMap.isEmpty()){ if(!this.geoms.isEmpty()){
Vector3d entityPosition = EntityUtils.getPosition(parent); Vector3d entityPosition = EntityUtils.getPosition(parent);
this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition));
for(String boneName : this.hitboxGeomMap.keySet()){ for(String boneName : this.boneHitboxMap.keySet()){
Vector3f bonePosition = EntityUtils.getPoseActor(parent).getBonePosition(boneName); Vector3f bonePosition = EntityUtils.getPoseActor(parent).getBonePosition(boneName);
DGeom geom = this.hitboxGeomMap.get(boneName); for(HitboxState state : this.boneHitboxMap.get(boneName)){
HitboxState shapeStatus = this.geomStateMap.get(geom); DGeom geom = this.stateGeomMap.get(state);
switch(shapeStatus.shapeType){ HitboxState shapeStatus = this.geomStateMap.get(geom);
case SPHERE: { switch(shapeStatus.shapeType){
this.updateSphereShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); case SPHERE: {
} break; this.updateSphereShapePosition(collisionEngine,boneName,shapeStatus,bonePosition);
case CAPSULE: { } break;
this.updateCapsuleShapePosition(collisionEngine,boneName,shapeStatus,bonePosition); case CAPSULE: {
} break; this.updateCapsuleShapePosition(collisionEngine,boneName,shapeStatus,bonePosition);
case STATIC_CAPSULE: { } break;
} break; case STATIC_CAPSULE: {
} break;
}
} }
} }
} else if(positionCallback != null){ } else if(positionCallback != null){
@ -313,7 +330,7 @@ public class HitboxCollectionState {
* @param bonePosition the position of the bone * @param bonePosition the position of the bone
*/ */
private void updateSphereShapePosition(CollisionEngine collisionEngine, String boneName, HitboxState hitboxState, Vector3f bonePosition){ private void updateSphereShapePosition(CollisionEngine collisionEngine, String boneName, HitboxState hitboxState, Vector3f bonePosition){
DGeom geom = this.hitboxGeomMap.get(boneName); DGeom geom = this.stateGeomMap.get(hitboxState);
//get offset's transform //get offset's transform
Vector3d offsetPosition = DataFormatUtil.getDoubleListAsVector(hitboxState.getHitboxData().getOffset()); Vector3d offsetPosition = DataFormatUtil.getDoubleListAsVector(hitboxState.getHitboxData().getOffset());
@ -344,7 +361,7 @@ public class HitboxCollectionState {
private void updateCapsuleShapePosition(CollisionEngine collisionEngine, String boneName, HitboxState hitboxState, Vector3f bonePosition){ private void updateCapsuleShapePosition(CollisionEngine collisionEngine, String boneName, HitboxState hitboxState, Vector3f bonePosition){
//get data about the hitbox //get data about the hitbox
DGeom geom = this.hitboxGeomMap.get(boneName); DGeom geom = this.stateGeomMap.get(hitboxState);
Vector3d previousWorldPos = hitboxState.getPreviousWorldPos(); Vector3d previousWorldPos = hitboxState.getPreviousWorldPos();
double length = hitboxState.getHitboxData().getRadius(); double length = hitboxState.getHitboxData().getRadius();
@ -418,7 +435,7 @@ public class HitboxCollectionState {
CollisionBodyCreation.attachGeomToBody(collisionEngine,body,geom); CollisionBodyCreation.attachGeomToBody(collisionEngine,body,geom);
} }
//update maps and other variables for next frame //update maps and other variables for next frame
this.hitboxGeomMap.put(boneName,geom); this.stateGeomMap.put(hitboxState,geom);
this.geomStateMap.put(geom,hitboxState); this.geomStateMap.put(geom,hitboxState);
this.geoms.add(geom); this.geoms.add(geom);
hitboxState.setPreviousWorldPos(worldPosition); hitboxState.setPreviousWorldPos(worldPosition);
@ -514,20 +531,115 @@ public class HitboxCollectionState {
} }
/** /**
* Gets the set of bone names in the state data * Gets the hitboxes associated with a bone
* @return The set of bone names in the state data * @param bone The bone
* @return The list of hitboxes if at least one is present, null otherwise
*/ */
public Set<String> getBones(){ private List<HitboxState> getHitboxes(String bone){
return this.hitboxGeomMap.keySet(); return this.boneHitboxMap.get(bone);
} }
/** /**
* Gets geometry on a single hitbox based on its bone name * Adds a hitbox to a bone
* @param boneName the bone name * @param bone The bone
* @return the hitbox geometry * @param state The hitbox
*/ */
public DGeom getGeometry(String boneName){ private void addHitbox(String bone, HitboxState state){
return this.hitboxGeomMap.get(boneName); if(this.boneHitboxMap.containsKey(bone)){
this.boneHitboxMap.get(bone).add(state);
} else {
List<HitboxState> states = new LinkedList<HitboxState>();
states.add(state);
this.boneHitboxMap.put(bone,states);
}
}
/**
* Registers a geometry
* @param geom The geom
* @param state The state associated with the geom
*/
private void registerGeom(DGeom geom, HitboxState state){
this.geoms.add(geom);
this.geomStateMap.put(geom,state);
this.stateGeomMap.put(state,geom);
}
/**
* Gets the hitbox type of a given hitbox data
* @param data The data
* @return The type of hitbox
*/
private static HitboxType getType(HitboxData data){
switch(data.getType()){
case HitboxData.HITBOX_TYPE_HIT: {
return HitboxType.HIT;
}
case HitboxData.HITBOX_TYPE_HURT: {
return HitboxType.HURT;
}
case HitboxData.HITBOX_TYPE_HIT_CONNECTED: {
return HitboxType.HIT;
}
case HitboxData.HITBOX_TYPE_HURT_CONNECTED: {
return HitboxType.HURT;
}
case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: {
return HitboxType.HURT;
}
default: {
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to parse undefined hitbox type " + data.getType()));
return HitboxType.HIT;
}
}
}
/**
* Gets the shape type of a hitbox data object
* @param data The data
* @return The shape type
*/
private static HitboxShapeType getShapeType(HitboxData data){
switch(data.getType()){
case HitboxData.HITBOX_TYPE_HIT:
case HitboxData.HITBOX_TYPE_HURT: {
return HitboxShapeType.SPHERE;
}
case HitboxData.HITBOX_TYPE_HIT_CONNECTED:
case HitboxData.HITBOX_TYPE_HURT_CONNECTED: {
return HitboxShapeType.CAPSULE;
}
case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: {
return HitboxShapeType.STATIC_CAPSULE;
}
default: {
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to parse undefined hitbox shape type " + data.getType()));
return HitboxShapeType.SPHERE;
}
}
}
/**
* Gets the hitbox subtype
* @param data The data
* @return The subtype
*/
private static HitboxSubtype getSubType(HitboxData data){
switch(data.getSubType()){
case HitboxData.HITBOX_SUBTYPE_SWEET: {
return HitboxSubtype.SWEET;
}
case HitboxData.HITBOX_SUBTYPE_REUGLAR: {
return HitboxSubtype.REGULAR;
}
case HitboxData.HITBOX_SUBTYPE_SOUR: {
return HitboxSubtype.SOUR;
}
default: {
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to parse undefined hitbox subtype " + data.getSubType()));
return HitboxSubtype.REGULAR;
}
}
} }
/** /**