Documentation Index Fetch the complete documentation index at: https://mintlify.com/toxicity188/BetterModel/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Hitboxes in BetterModel allow you to create interactive regions on your model that respond to player clicks, attacks, and entity mounting. They’re implemented using invisible entities synchronized with bone positions.
Understanding Hitboxes
What are Hitboxes?
Hitboxes are invisible collision boxes attached to model bones that:
Detect player interactions (right-click, left-click, attack)
Support entity mounting and vehicles
Follow bone transformations (position, rotation, animation)
Can be created automatically or manually
Hitbox Types
BetterModel uses different entity types for hitboxes:
Interaction entities (1.19.4+): Precise click detection
Slimes : Compatibility for older versions
Automatic Hitbox Creation
Tag bones in BlockBench with hitbox to auto-create hitboxes:
model_root
├── head [tag: hitbox]
├── body [tag: hitbox]
└── mount_point [tag: hitbox]
Hitboxes are created automatically when the tracker spawns.
Using Bone Names
Name a bone hitbox to auto-create:
model_root
└── hitbox # Auto-creates hitbox
Manual Hitbox Creation
Creating a Single Hitbox
Create hitboxes programmatically using bone predicates:
import kr.toxicity.model.api.tracker.EntityTracker;
import kr.toxicity.model.api.util.function.BonePredicate;
import kr.toxicity.model.api.nms.HitBoxListener;
EntityTracker tracker = // your tracker
// Create hitbox on bones named "head"
boolean created = tracker . createHitBox (
null , // HitBoxListener (null for default)
BonePredicate . name ( "head" )
);
if (created) {
System . out . println ( "Hitbox created" );
}
Creating with Custom Listener
Add custom interaction handling:
import kr.toxicity.model.api.nms.HitBoxListener;
HitBoxListener listener = HitBoxListener . builder ()
. interact (event -> {
System . out . println ( "Player interacted: " + event . player (). name ());
System . out . println ( "Hand: " + event . hand ());
})
. interactAt (event -> {
System . out . println ( "Interaction position: " + event . position ());
})
. attack (event -> {
System . out . println ( "Hitbox attacked by: " + event . player (). name ());
})
. build ();
tracker . createHitBox (
listener,
BonePredicate . name ( "body" )
);
Hitbox Events
Available Events
import kr.toxicity.model.api.event.hitbox. * ;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class HitBoxEventListener implements Listener {
@ EventHandler
public void onHitBoxCreate ( HitBoxCreateEvent event ) {
System . out . println ( "Hitbox created: " + event . bone (). name ());
}
@ EventHandler
public void onHitBoxRemove ( HitBoxRemoveEvent event ) {
System . out . println ( "Hitbox removed" );
}
@ EventHandler
public void onHitBoxInteract ( HitBoxInteractEvent event ) {
System . out . println ( "Player: " + event . player (). name ());
System . out . println ( "Hand: " + event . hand ());
// Cancel the event to prevent default behavior
event . cancelled ( true );
}
@ EventHandler
public void onHitBoxInteractAt ( HitBoxInteractAtEvent event ) {
System . out . println ( "Interaction position: " + event . position ());
}
@ EventHandler
public void onHitBoxAttack ( HitBoxAttackEvent event ) {
System . out . println ( "Attacker: " + event . player (). name ());
}
@ EventHandler
public void onHitBoxMount ( HitBoxMountEvent event ) {
System . out . println ( "Entity mounted: " + event . entity (). uuid ());
}
@ EventHandler
public void onHitBoxDismount ( HitBoxDismountEvent event ) {
System . out . println ( "Entity dismounted" );
}
}
Tracker-Level Event Listening
Register event listeners directly on a tracker:
import kr.toxicity.model.api.event.hitbox.HitBoxInteractEvent;
tracker . listenHitBox ( HitBoxInteractEvent . class , event -> {
System . out . println ( "Hitbox clicked!" );
event . player (). sendMessage ( "You clicked the model!" );
});
// Or using builder pattern
tracker . listenHitBox ((bone, builder) -> builder
. interact (event -> {
System . out . println ( "Interacted with: " + bone . name ());
})
. attack (event -> {
System . out . println ( "Attacked: " + bone . name ());
})
);
Working with Hitboxes
Retrieving Hitboxes
Get a hitbox from a bone:
import kr.toxicity.model.api.bone.RenderedBone;
import kr.toxicity.model.api.nms.HitBox;
// Get specific bone's hitbox
RenderedBone bone = tracker . bone ( "head" );
if (bone != null ) {
HitBox hitBox = bone . getHitBox ();
if (hitBox != null ) {
System . out . println ( "Hitbox exists for head" );
}
}
// Or retrieve/create in one call
HitBox hitBox = tracker . hitbox (
tracker . sourceEntity (),
null , // listener
bone -> bone . name (). toString (). equals ( "head" )
);
Accessing from Registry
Get all hitboxes for an entity:
import kr.toxicity.model.api.tracker.EntityTrackerRegistry;
BetterModel . registry ( entity . getUniqueId ()). ifPresent (registry -> {
registry . hitBoxCache (). forEach ((uuid, hitBox) -> {
System . out . println ( "Hitbox: " + hitBox . groupName ());
});
});
Removing Hitboxes
Remove a specific hitbox:
HitBox hitBox = // your hitbox
hitBox . removeHitBox ();
Mounting System
Mount Controllers
Control mounting behavior:
import kr.toxicity.model.api.mount.MountController;
import kr.toxicity.model.api.mount.MountControllers;
HitBox hitBox = // your hitbox
// Allow mounting
hitBox . mountController ( MountControllers . ALLOW );
// Allow mounting and control
hitBox . mountController ( MountControllers . CONTROL );
// Disable mounting
hitBox . mountController ( MountControllers . DENY );
Mounting Entities
Mount an entity to a hitbox:
import kr.toxicity.model.api.platform.PlatformEntity;
PlatformEntity entity = BukkitAdapter . adapt (player);
HitBox hitBox = // your hitbox
// Mount the entity
hitBox . mount (entity);
// Check if mounted
if ( hitBox . hasMountDriver ()) {
System . out . println ( "Entity is mounted" );
}
// Dismount
hitBox . dismount (entity);
// Dismount all
hitBox . dismountAll ();
Mount Events
Listen for mount/dismount:
import kr.toxicity.model.api.event.MountModelEvent;
import kr.toxicity.model.api.event.DismountModelEvent;
@ EventHandler
public void onMount ( MountModelEvent event) {
EntityTracker tracker = event . tracker ();
RenderedBone bone = event . bone ();
HitBox hitBox = event . hitBox ();
PlatformEntity entity = event . entity ();
System . out . println ( entity . name () + " mounted " + bone . name ());
}
@ EventHandler
public void onDismount ( DismountModelEvent event) {
System . out . println ( "Entity dismounted" );
}
Vehicle Movement
Check if passenger is walking:
HitBox hitBox = // your hitbox
if ( hitBox . onWalk ()) {
System . out . println ( "Passenger is walking" );
// Play walking animation
tracker . animate ( "walk" );
}
Advanced Hitbox Usage
Conditional Hitbox Creation
Create hitboxes based on conditions:
tracker . createHitBox (
null ,
BonePredicate . tag ( "clickable" )
. and (bone -> bone . name (). toString (). startsWith ( "interactive_" ))
);
Dynamic Hitbox Listeners
Change hitbox behavior at runtime:
// Track hitbox state
Map < HitBox , Integer > clickCounts = new HashMap <>();
tracker . listenHitBox ((bone, builder) -> builder
. interact (event -> {
HitBox hitBox = event . hitBox ();
int clicks = clickCounts . getOrDefault (hitBox, 0 ) + 1 ;
clickCounts . put (hitBox, clicks);
if (clicks >= 3 ) {
System . out . println ( "Unlocked after 3 clicks!" );
hitBox . removeHitBox ();
}
})
);
Multi-Hitbox Interactions
Create complex interaction patterns:
public class PuzzleModel {
private final Set < String > activatedBones = new HashSet <>();
public void setupPuzzle ( EntityTracker tracker ) {
tracker . listenHitBox ((bone, builder) -> builder
. interact (event -> {
String boneName = bone . name (). toString ();
if ( boneName . startsWith ( "button_" )) {
activatedBones . add (boneName);
// Visual feedback
tracker . update (
TrackerUpdateAction . tint ( 0x00FF00 ),
b -> b . name (). toString (). equals (boneName)
);
// Check completion
if ( activatedBones . size () == 4 ) {
completePuzzle (tracker);
}
}
})
);
}
private void completePuzzle ( EntityTracker tracker ) {
tracker . animate ( "unlock" , AnimationModifier . DEFAULT_WITH_PLAY_ONCE );
}
}
Hitbox Visibility Control
Show/hide hitboxes for specific players:
import kr.toxicity.model.api.platform.PlatformPlayer;
HitBox hitBox = // your hitbox
PlatformPlayer player = BukkitAdapter . adapt (bukkitPlayer);
// Hide from player
hitBox . hide (player);
// Show to player
hitBox . show (player);
Hitbox Best Practices
Performance Tips:
Limit hitboxes to interactive areas only
Use bone tags for automatic creation
Avoid creating/destroying hitboxes frequently
Cache hitbox references when possible
Common Pitfalls:
Hitboxes follow animations - ensure bones are properly positioned
Large hitbox counts can impact performance
Mount points require proper pivot positioning
Hitbox size is determined by bone group configuration
Naming Conventions
Use clear, descriptive bone names:
interactive_chest_lid
clickable_button_red
mount_seat_driver
hitbox_head_damage
Size Configuration
Configure hitbox size in bone groups:
# In your model configuration
groups :
head :
hitbox :
width : 0.8
height : 0.8
depth : 0.8
Debugging Hitboxes
Visual Debugging
Make hitboxes visible temporarily:
// Set hitbox to visible (for debugging)
// Note: This requires NMS access
tracker . bones (). forEach (bone -> {
HitBox hitBox = bone . getHitBox ();
if (hitBox != null ) {
System . out . println ( "Hitbox at: " + bone . name ());
System . out . println ( "Position: " + hitBox . relativePosition ());
}
});
Logging Interactions
tracker . listenHitBox ((bone, builder) -> builder
. interact (event -> {
System . out . println ( "=== Hitbox Interaction ===" );
System . out . println ( "Bone: " + bone . name ());
System . out . println ( "Player: " + event . player (). name ());
System . out . println ( "Hand: " + event . hand ());
System . out . println ( "=========================" );
})
);
Example: Interactive NPC
public class InteractiveNPC {
public void createNPC ( Location location ) {
DummyTracker tracker = BetterModel . model ( "npc_villager" )
. map (r -> r . create ( BukkitAdapter . adapt (location)))
. orElse ( null );
if (tracker == null ) return ;
// Create clickable head
tracker . listenHitBox ((bone, builder) -> {
if ( bone . name (). toString (). equals ( "head" )) {
return builder . interact (event -> {
PlatformPlayer player = event . player ();
// Play greeting animation
tracker . animate ( "wave" , AnimationModifier . DEFAULT_WITH_PLAY_ONCE );
// Send message
player . sendMessage ( "Hello, " + player . name () + "!" );
});
}
return builder;
});
// Create hitboxes
tracker . task (() -> tracker . createHitBox (
null ,
BonePredicate . name ( "head" )
));
}
}
Next Steps
Player Models Work with player limb models and customization
Per-Player Animation Individual animation states per player