miaoda-game-combat2d-core
v0.2.0
Published
Engine-agnostic real-time hit-resolution core: hitbox/hurtbox overlap with team layer/mask filtering (Godot-style), once-per-attack hit dedup, invincibility frames, knockback vectors and pierce. Turns the raw fact 'two boxes overlap' into 'who hit whom, f
Maintainers
Readme
miaoda-game-combat2d-core
Use this package to turn 2D overlaps into deterministic combat events. It applies team masks, once-per-attack deduplication, invincibility frames, and pierce limits. It does not move bodies, calculate damage, render, or decide what a hit means.
Install
pnpm add miaoda-game-combat2d-coreRegister boxes and resolve a frame
import { CombatField, aabb, layerBit } from 'miaoda-game-combat2d-core';
const PLAYER = layerBit(0);
const ENEMY = layerBit(1);
const field = new CombatField();
field.addHurtbox({
id: 'slime', x: 100, y: 40, shape: aabb(8, 8),
layer: ENEMY, enabled: true, owner: slime,
});
field.addHitbox({
id: 'sword', x: 92, y: 40, shape: aabb(12, 6),
mask: ENEMY, enabled: true, attackId: 'swing-42',
damage: 7, knockback: { x: 120, y: -40 }, owner: player,
});
field.update('slime', slime.x, slime.y);
field.update('sword', swordX, swordY);
for (const hit of field.resolve(6)) {
stats.applyDamage(hit.target, hit.damage);
movement.knockback(hit.target, hit.knockback);
}Call update for changed positions, then call resolve once per logic frame. A hitbox connects only when hitbox.mask & hurtbox.layer is non-zero. resolve returns accepted HitEvents; your game routes their payload to stats, status, movement, hitstop, or effects.
Combat rules
- Attack deduplication: keep the same
attackIdwhile a sword arc overlaps across several frames. It can hit that target once; use a new ID for the next swing. - Invincibility frames:
resolve(defaultIframes)grants frames to accepted targets. UsegrantIframesfor dodges, revives, or scripted protection. - Pierce: a hitbox defaults to one target. Set
pierceto a larger value or-1for unlimited targets such as explosions and lasers. - Geometry:
aabb,circle, andshapesOverlapcover headless AABB/circle checks. An engine adapter can instead pass broadphase pairs toresolveContacts.
Broadphase and contacts
const hits = field.resolveContacts([
{ hitboxId: 'sword', hurtboxId: 'slime' },
], 6);Use contacts from Cocos or Phaser physics when scanning every registered pair is too broad. Core masks, deduplication, i-frames, and pierce still apply; input order decides which target consumes a limited pierce budget first.
API guide
| API | Use it for |
| --- | --- |
| addHitbox / addHurtbox | Register attack and target shapes |
| update | Move a registered box before resolving |
| resolve | Scan registered geometry for one logic frame |
| resolveContacts | Resolve pairs supplied by an engine broadphase |
| grantIframes / isInvincible | Add or inspect target protection |
| resetAttack | Allow a persistent hitbox to use an attack ID again |
| snapshot | Inspect stable combat state for tests and telemetry |
| layerBit / ALL_LAYERS | Build team and collision masks |
The package owns combat acceptance, not physics response. Pair it with miaoda-game-stats-core for damage formulas, miaoda-game-status-core for effects, miaoda-game-beam-core or miaoda-game-shooter-core for attack generation, and a movement package for knockback.
