Log inGet started
← Explore
World media

Second Wind: Health, Damage and Respawn Kit

Give anything in your game health, damage, death and respawn with one component. Walk the gallery to see it all running: six health displays (HP bar, regenerating shield, armour, hearts, lives, downed-then-revive), eight damage types with flames, sparks and damage numbers every player sees, eight death styles from instant respawn to ragdoll and dissolve, a kill feed in five looks (minimal, sci-fi, fantasy, cartoon, pixel), traps, teams and genre presets from shooter to party game. To use it in your own world, install the SecondWind package, then install Lifeline and MatchFlow into /source in a second step, and start from the README's five-minute lava pit example.

0score
Embed

Embed this world

Paste this code into your own page and the world plays right there.

Updated Oct 17, 2026
healthdamagerespawncombatmultiplayerkithudkill-feed

SecondWind 1.2.0

Health, damage, death and respawn for any multiplayer game, in one component. Give players, enemies and breakable props health with one call, hurt them with another, and choose everything else with a setting: six health displays (a plain HP bar, a regenerating shield, armour, Zelda-style hearts, lives, or downed-then-revive), eight damage types with flames, sparks and numbers every player sees, eight death styles, teams and friendly fire, a kill feed in five looks, traps, and genre presets from shooter to party game, all labelled in a catalog you can filter by genre and visual style. Built on Lifeline (health, bars, numbers) and MatchFlow (countdowns, rounds), and multiplayer-safe.

What's new in 1.2.0

The package is now called SecondWind, like this world, and it sits beside Lifeline and MatchFlow. It used to be called healthKit. It is still the same package with the same guid (5da0a53c-5953-4adc-80fb-f5afb02f6568), and every call, setting, event, preset and network channel works exactly as in 1.1.4.

  • New require line: local HK = require("SecondWind.api"), in place of require("healthKit.api").
  • New asset names: the components are SecondWind.Vitals, SecondWind.Rules, SecondWind.HUD and SecondWind.Trap, the modules are SecondWind.catalog, SecondWind.logic, SecondWind.feed and SecondWind.fx, and the test suite is secondwind.
  • Upgrading from 1.1.x: after you update, change healthKit. to SecondWind. in your require and asset.resolve lines. An installed copy stays at the version it was installed at, so a world on 1.1.4 keeps working until it updates.
  • Kept on purpose: the network channels keep their names (healthKit:evts, healthKit:state and the rest), and the HUD's screen id stays healthkit_hud, so nothing a game sends, listens for or looks up changes.
  • A store listing: the package description now says what the kit gives a creator, its six health displays and eight death styles, and how to install it with Lifeline and MatchFlow.

What's new in 1.1.4

Every call, setting, event and preset works exactly as in 1.1.1. This version fixes the first example and the descriptions.

  • The first example works as pasted. Its dummy stood outside the lava and above it, and nothing in the kit moves a dummy, so it couldn't be pushed in and never burned. It now stands in the edge of the lava, on the ground, and an orange slab shows where the lava is. Checked in a new world holding only the two-call install: the dummy went from 60 to 40 to 20 to dead in about 4 s, came back with full hearts where it stood 4 s later, and did it again; a player standing in the lava burned from 150 to 95 in 3 s.
  • Second Wind opens in the gallery again. 1.1.3 saved a small test scene as the world's main scene by mistake, so the editor opened in it.
  • Known limits names the public engine issue #19188 for the install problem, instead of two internal ticket ids.
  • The package description says eight death styles, as the catalog and this README do. It said six and left out remove and stay, and it called the component Health instead of Vitals.

What's new in 1.1.3

Every call, setting, event and preset works exactly as in 1.1.1. This version corrects step 1 again.

  • Install in two calls. The one-call install that 1.1.2 gave doesn't work. When SecondWind, Lifeline and MatchFlow go in one call, the last two only update the copies SecondWind brought under /source/deps/ and nothing is installed in /source, so SecondWind still can't reach them. Step 1 now installs SecondWind first, then Lifeline and MatchFlow in a second call with at=/source. Checked with the published kit in a new world: after the one call, module 'Lifeline.api' not found and five publish errors remained. After the second call, the scene built, the player got 150 HP, the lava burned, the hearts dummy died and came back, and zm push published the world. If you followed 1.1.2's step 1, run the second call now; it fixes the world in place.

What's new in 1.1.2

Every call, setting, event and preset works exactly as in 1.1.1. This version fixes the install instructions.

  • The first example works in a brand-new world. Step 1 used to say Lifeline and MatchFlow come along with SecondWind. In a new world they do arrive, but SecondWind can't reach them: its components fail with module 'Lifeline.api' not found and the world can't be published (see Known limits). Step 1 now installs Lifeline and MatchFlow as well. Checked in a new world holding only those three installs: the scene builds, the player gets 150 HP from the heroShooter preset, the lava burns, the hearts dummy dies and comes back, the health bar shows, and zm push publishes the world. Earlier versions were only checked in the world that authors the kit, which has both installed.

What's new in 1.1.1

Every 1.0.0 and 1.1.0 call, setting, event and preset works as before. New in this version:

  • Fewer messages per hit. Events for other peers wait up to 0.05 s and leave together as one healthKit:evts message, in order, so a burst (a chain, a blast, a kill and its death) is one message instead of one per event. Listeners on the peer that raised them (HK.on) still hear each event at once, and a health snapshot never overtakes the events queued before it. 1.1.1 still receives the one-per-event healthKit:evt messages that 1.1.0 peers send, so mixed sessions work.
  • HK.syncReport().totals also counts evtSent (event batches sent) and evtBatched (the events inside them).
  • A lighter showcase gallery. Its dummies and damage stations only work while a player is within 18 m (wakeRadius on the Gallery component). The dummies drain just as fast with half as many hits, the kill-feed boards rebuild only when the feed changes, signs and pads refresh a few times a second instead of every frame, and name labels are looked up by name instead of by scanning every entity. With a player inside, the gallery's host now sends about 12 to 14 messages a second instead of about 46, a damage request through a browser host comes back in 0.2 to 0.4 s instead of 25 s or more, and on a peer that doesn't host it the Gallery script costs 0.07 ms a frame.
  • Gallery labels stay hidden after a scene rebuild. Right after a rebuild the engine can briefly refuse to switch a {PlayerName} label off; the gallery now retries until it holds.

What's new in 1.1.0

Every 1.0.0 call, setting, event and preset works as before. New in this version:

  • A labelled catalog. Every health model, damage type, death style, kill feed look and preset now says its kind, its family, the genres it suits, whether it has a look of its own (hasLook) and, if so, its style. HK.find({ kind = "preset", genre = "coop" }) picks by label, and HK.vocabulary() lists the words. See Picking from the catalog.
  • Hit effects for everyone. Flames, sparks, bubbles and frost now show on every player's screen, not just for the player who landed the hit. Each hit is also written into the target's synced hitSeq, hitType and hitAmount, so a peer that misses the hit message still draws the effect.
  • One health on every screen. On every death, respawn, revive, life lost and knock-down, the owner sends a full state snapshot (healthKit:state) at once, again after 0.5 s and again after 2 s. It also sends one after any change and every 5 s regardless. A peer whose view stays off for more than half a second asks for a fresh one (healthKit:resync). HK.state gives the latest agreed snapshot, and HK.syncReport() shows how sync is doing.
  • Fall damage fixed around death and respawn. Fall tracking now starts fresh on death, on respawn and on any teleport, with one second of grace after a respawn. A player who died from a fall no longer takes a second fall hit when they reappear, and their own screen and everyone else's agree.
  • A clean gallery and a new cover. The gallery dummies no longer show a {PlayerName} placeholder, in play or in the editor.

Install and use (five minutes)

  1. Install. Install three packages from ZeroMind, in two calls:

    • first SecondWind: zeromind_install guid=5da0a53c-5953-4adc-80fb-f5afb02f6568
    • then the two it is built on, Lifeline (7b85ce05-3903-4374-b654-2d4ffea295a5) and MatchFlow (31cf13b4-df7d-4888-85e2-7947a7301614), into /source: zeromind_install guid=7b85ce05-3903-4374-b654-2d4ffea295a5,31cf13b4-df7d-4888-85e2-7947a7301614 at=/source

    Don't skip the second call, don't merge the two, and keep at=/source. SecondWind brings its own copies of Lifeline and MatchFlow but the engine doesn't connect it to them yet, and in one call, or without at, the second install only updates those copies (see Known limits). While Lifeline installs, the log may say '~Lifeline.api' is ambiguous for a moment; that clears when the install finishes.

  2. Set it up in your scene's build.luau. A new world's main scene (or one made with the player scene template) builds its player in build.luau with a line like local body = entity.spawn("body", { parent = prototype }). Add these lines at the end of content(), after that player setup:

local HK = require("SecondWind.api")

-- World rules + on-screen HUD (kill feed, death screen, bars). Rules must be synced.
local rules = entity.spawnSynced("HealthRules")
rules.component.add(HK.RULES, { preset = "heroShooter" })   -- or leave preset empty
entity.spawn("HealthHUD").component.add(asset.resolve("SecondWind.HUD", "component"), {})

-- Players: give the PlayerPrototype's body health. Every joining player gets it.
HK.attach(body, { team = "blue" })          -- `body` from the default player setup

-- A trap: lava that burns anyone standing in it (a 3 x 1 x 3 m box on the ground).
local lava = entity.spawnSynced("Lava", { position = { 0, 0.5, 4 } })
lava.component.add(asset.resolve("SecondWind.Trap", "component"),
	{ damageType = "fire", label = "Lava", amount = 15, sizeX = 3, sizeZ = 3 })
-- An orange slab on the ground, so you can see where the lava is.
local glow = entity.spawn("LavaGlow", { position = { 0, 0.05, 4 } })
glow.component.add("Model", { model = "cube", material = asset.resolve("@builtin::materials.plastic_white", "material"),
	tintR = 1, tintG = 0.35, tintB = 0.05, tintBlend = 1 })
glow.localScale = { 3, 0.1, 3 }

-- A dummy standing in the edge of the lava: it burns, dies, and comes back where it stood.
local dummy = entity.spawnSynced("Dummy", { position = { 1, 0.5, 4 } })
dummy.component.add("Model", { model = "cube" })
HK.attach(dummy, { model = "hearts", deathStyle = "timed", displayName = "Dummy" })
  1. Press Play. The lava is the orange slab about 4 m in front of where you start, and the dummy stands in its edge. The dummy catches fire, loses its hearts, dies, and comes back where it stood 4 seconds later, then burns again. Walk onto the slab and you catch fire too: flames, damage numbers, BURNING on screen and your bar dropping from 150. Stay in and you die and come back at the spawn point after the death screen's countdown.

Hurting something from code is one call:

HK.damage(target, 20, { by = attacker, type = "fire", team = "red" })

Switching anything is one setting, even while the game runs:

HK.setModel(target, "shield")         -- or hearts, lives, downed, armor, hitPoints
HK.setDeathStyle(target, "spectate")  -- or instant, timed, wave, ragdoll, dissolve, remove, stay
HK.applyPreset("battleRoyale")        -- the whole world at once

HK.catalog() lists everything below, with descriptions and labels.

Picking from the catalog

Every entry (model, damage type, death style, feed look, preset) carries these labels:

labelvalues
kindhealthModel, damageType, deathStyle, feedLook, preset
familyhealth models: pool, counted, secondChance. Damage types: direct, elemental, world. Death styles: respawn, deathAnimation, outOfPlay. Feed looks: killFeed. Presets: competitive, adventure, cooperative, casual
genresa list from shooter, heroShooter, battleRoyale, adventure, roguelike, rpg, coop, survival, platformer, party
hasLooktrue when the entry draws something of its own
stylefor entries with a look: minimal, scifi, fantasy, cartoon, pixel (a preset takes the style of its kill feed look)
local HK = require("SecondWind.api")

HK.find({ kind = "preset", genre = "coop" })                 -- { coopRevive entry }
HK.find({ kind = "feedLook", style = { "pixel", "cartoon" } }) -- any of the listed values
HK.find({ kind = "healthModel", family = "counted" })        -- hearts, lives
HK.find({ genre = "roguelike" })                             -- everything that suits a roguelike

for _, p in HK.find({ kind = "preset", genre = "battleRoyale" }) do
	print(p.id, p.family, p.style, table.concat(p.genres, ", "))
end

HK.vocabulary()  -- { kinds, families, genres, styles }, each word with a label and description

Every key is optional and takes one value or a list (any of them matches). An unknown key or kind is an error that names the valid ones, so a typo never just returns nothing. SecondWind.catalog also offers C.filter(kind, query) for one kind (a kind id like "preset" or a list name like "presets").

What it offers

Health models (model)

idwhat it does
hitPointsOne health bar.
shieldA shield soaks damage first and recharges after shieldRegenDelay seconds without a hit. Electric damage does double to shields.
armorAn armour pool absorbs armorAbsorb of each physical, fire, ice, electric or explosion hit and wears down. Poison, fall and environment damage go around it.
heartsZelda-style hearts with half hearts (one half heart = 10 HP). Every hit costs at least half a heart and rounds up to whole half hearts.
liveslives lives, each with a pool of lifeHealth. Emptying a pool spends a life and refills it. Losing the last life is death.
downedA lethal hit knocks you down instead. You crawl at reduced speed and bleed out over bleedOutTime. A living teammate who stands within reviveRadius for reviveTime picks you up.

Damage types (type)

idbehaviourlooks like
physicalA plain hit.white sparks
fireKeeps burning for a few seconds after the hit. Ice puts it out.rising flames; burning targets trail embers
poisonStacks up to 5. Each stack ticks every second, and a new hit refreshes them. Goes around armour.poison bubbles; poisoned targets trail bubbles
iceSlows the target to half speed for a few seconds. Puts out fire.ice shards; chilled targets trail frost
electricChains to up to 3 nearby targets at 60% damage. Double damage to shields.electric sparks and arcs to each chained target
explosionFalls off with distance and knocks targets back (HK.explode).an expanding explosion shell
fallTaken automatically when landing faster than Rules.fallSafeSpeed. Goes around armour.landing dust
environmentLava, spikes, acid: the world itself (SecondWind.Trap). Goes around armour.red spikes

Death styles (deathStyle)

idwhat happens
instantBack at a spawn point straight away.
timedBack at a spawn point after respawnDelay seconds, with a MatchFlow countdown on screen.
waveEveryone who died comes back together on the next wave, every Rules.waveInterval seconds.
ragdollThe body is flung and tumbles limp, then respawns after respawnDelay.
dissolveThe body sinks and dissolves into sparks, then respawns after respawnDelay.
spectateStays dead and watches a living player until the round ends (HK.endRound(), a MatchFlow round ending, or Rules.autoEndRound).
removeGone for good: for breakable props and one-shot enemies.
stayStays dead until a script calls HK.revive.

Presets (preset)

idmodeldeath stylefeedfriendly fire
arcadeShooterhitPointsinstantpixeloff
heroShootershield (150 HP + 75 shield)timed (5 s)scifioff
adventureHeartshearts (5)dissolvefantasyoff
roguelikeLiveslives (3 × 40 HP)ragdollpixeloff
coopRevivedowned (20 s bleed-out)waveminimaloff
battleRoyalearmorspectateminimalon
partyBrawlhitPoints, huge knockbackragdollcartoonon

Kill feed looks (feedLook)

minimal, scifi, fantasy, cartoon, pixel. Rows read like "Nova burned Kai" or "Kai was killed by Lava".

Reference

How settings resolve

For each entity, each value is taken from the first of these that sets it:

  1. The entity's own Vitals fields. A model or deathStyle of auto means "not set".
  2. The entity's own preset.
  3. For player bodies only: the world Rules (playerModel, playerDeathStyle, and the preset's tuning).
  4. For everything else: hitPoints and Rules.otherDeathStyle.

HK.config(target) returns the resolved result. A model switch keeps the current health fraction.

SecondWind.api (local HK = require("SecondWind.api"))

Every target argument accepts an entity, a component or an entity id.

callwhat it does
HK.damage(target, amount, opts?) -> ok, reasonHurt a target. opts: by, type, team, origin {x,y,z}, knockback (m/s), crit, dot, label ("Lava"), ignoreInvulnerability, status (false = no burn/poison/chill).
HK.heal(target, amount, opts?) -> ok, reasonRestore health. Hearts round up to half hearts. opts: by.
HK.kill(target, opts?) -> ok, reasonKill outright, skipping lives and downed. opts: by, type.
HK.revive(target, opts?) -> ok, reasonPick a downed target up, or respawn a dead one now.
HK.explode(pos, opts?) -> countExplosion with falloff and knockback. opts: radius (5), damage (60), by, team, knockback (10), minFalloff (0.2).
HK.attach(entity, settings?) -> VitalsAdd Lifeline.Health and SecondWind.Vitals with these settings. Use it in a build or on a freshly spawned entity.
HK.configure(target, settings) -> ok, reasonChange any Vitals settings at runtime.
HK.setModel(target, model) / HK.setDeathStyle(target, style)Switch one setting. Pass "auto" to follow the preset or the rules again.
HK.applyPreset(name, target?)With no target, sets the world preset. With a target, sets that entity's own preset.
HK.setRules(t) -> ok, reasonChange world rules, e.g. { friendlyFire = true }.
HK.endRound()End the round: spectators come back.
HK.state(target)Snapshot in the terms of its model: health, maxHealth, fraction, shield, maxShield, armor, maxArmor, hearts, heartsLeft, lives, livesLeft, downed, bleedLeft, reviveProgress, dead, deaths, respawnIn, dyingStyle, burning, poisonStacks, chilled, model, deathStyle, preset, team, name.
HK.isAlive(target) / HK.isDowned(target)Booleans. A downed target counts as alive.
HK.config(target)The resolved settings (model, deathStyle, preset, all tuning).
HK.get(target) / HK.lifeline(target) / HK.all()The Vitals, the Lifeline.Health underneath, and every Vitals keyed by id.
HK.rules() / HK.ruleValues()The Rules component, and its values as a table (defaults when there is no Rules entity).
HK.nameOf(target) / HK.isPlayer(id) / HK.idOf(x)Display name, player-body test, and id normaliser.
HK.on(event, fn) -> connListen to an event on this peer ("*" = all). conn:Disconnect() stops it.
HK.killFeed()Recent kill feed entries on this peer.
HK.setDamageFilter(fn?)fn(vitals, amount, info) -> amount, run on the owner after team checks. Return 0 to cancel.
HK.catalog()Every model, damage type, death style, preset, feed look and event, plus version and vocabulary. Every entry carries its labels.
HK.find(query?) -> { entry }Catalog entries matching labels: kind, family, genre, style, hasLook, id. Each takes one value or a list.
HK.vocabulary()The words the labels use: kinds, families per kind, genres and styles.
HK.syncReport()How health sync is doing on this peer: totals (sent, asked, served, resyncs, snapshots, evtSent, evtBatched), the entities this peer owned and the ones it watched (in sync, seconds off, latest snapshot).
HK.VERSION, HK.HEALTH, HK.RULES, HK.logicThe version string, the Vitals and Rules component assets, and the pure rules maths.

Events (HK.on)

damaged, healed, blocked (reason: immune, friendly, self, armor, dead), lifeLost, downed, revived, died, killed (a kill feed entry: killer, killerName, victim, victimName, type, text), respawned, statusApplied, chained, exploded, wave, roundEnded. They fire on every peer.

SecondWind.Vitals component

Settings: model, deathStyle, preset, team, displayName, barStyle, startFull, invulnerable, fallDamage, canRevive. Tuning: maxHealth 100, regenRate 0, maxShield 50, shieldRegenRate 20, shieldRegenDelay 3, maxArmor 50, armorAbsorb 0.66, hearts 3, lives 3, lifeHealth 30, bleedOutTime 15, reviveTime 3, reviveRadius 2, reviveHealth 0.3, respawnDelay 4, invulnerability 0.5 (i-frames after a hit), knockbackScale 1. Read-only synced state: armor, livesLeft, downed, bleedLeft, reviveProgress, reviver, respawnIn, dyingStyle, burning, poisonStacks, chilled, activeModel, activeStyle, activePreset, hitSeq, hitType, hitAmount (the last hit, for its effect), changeSeq (bumped on every health change). Methods: damage(amount, opts), heal(amount, opts), kill(opts), revive(opts). It needs Lifeline.Health on the same entity; awake adds it when missing.

SecondWind.Rules component (one per scene, on a synced entity)

preset, friendlyFire (off), selfDamage (on), playerModel (hitPoints), playerDeathStyle (timed), otherDeathStyle (timed), feedLook (minimal), waveInterval 10, autoEndRound (on), matchEntity ("Match", a MatchFlow entity whose round end ends the SecondWind round), maxRemoteDamage 1000, fallSafeSpeed 10, fallDamagePerSpeed 7, allowClientConfig (off). Read-only: waveIn, waveSeq, roundSeq.

SecondWind.HUD component (per peer, local)

feedLook (rules follows Rules.feedLook), showFeed, maxFeed 5, feedTime 6, showDeathScreen, showStatus, showDownedMarkers, countdownStyle (any MatchFlow countdown style; default bold), spectateSwitch 8, addLifelineHUD, autoBarStyle. Method: nextTarget() switches the spectated player.

SecondWind.Trap component

A box volume that hurts what enters it: damageType, label, amount, mode (perSecond | onEnter), interval, sizeX/Y/Z, knockback, team, by, playersOnly, active. It runs on the authority of each victim, so put it on any entity, synced or not.

Other modules

  • SecondWind.catalog: the data behind HK.catalog().
  • SecondWind.logic: pure maths (pools, armour and shield split, falloff, fall damage, team rule, feed text).
  • SecondWind.feed: render(look, entries, opts) for your own UI.
  • SecondWind.fx: burst(type, pos, amount) hit effects.

Multiplayer

  • Authority: each health is owned by the peer that owns its entity. A player's body belongs to that player, and synced world entities belong to the host. Only the owner changes health. All state is synced fields, so every peer sees the same health, deaths and respawns.
  • Remote calls: HK.damage on a target you don't own sends a request to its owner. The owner checks that you own the attacker you named, applies team and friendly-fire rules, and caps the amount at maxRemoteDamage.
  • Admin calls: configure, kill, revive and endRound from another peer are refused unless that peer owns the Rules entity or allowClientConfig is on.
  • Events: effects and events run on every peer. Events for other peers leave in batches on healthKit:evts, at most one message every 0.05 s, in the order they were raised; healthKit:evt messages from 1.1.0 peers are still received. Hit effects are drawn on the next frame from a queue, and each hit is drawn once per peer however it arrives (event or synced hitSeq).
  • State snapshots: the owner sends the full health state on the healthKit:state channel. It sends right away on every death, respawn, revive, life lost and knock-down, then again after 0.5 s and 2 s. It also sends after changes (at most once a second for players, every 3 s for others) and every 5 s regardless. Snapshots are batched, about 2 to 3 messages a second for a busy scene. A peer whose fields disagree with the last snapshot for over 0.5 s asks for a fresh one on healthKit:resync. All the kit's channels are registered when the kit loads; you do not call them yourself.

Tests

The SecondWind.secondwind test suite has 87 tests covering every public call: catalog labels and filtering, hit effects reaching peers, fall damage around death and respawn, health sync, event batching, the README's install step and first example, the package description, and the 1.2.0 rename (new names, same guid, no old requires left, channels kept). Run it with tools.use("tests", "run", { suite = "secondwind" }).

Known limits

  • #2612: a package cannot list Lifeline as a dependency. package.yaml names no dependencies, so installing SecondWind doesn't install Lifeline and MatchFlow as packages of your world. Install them yourself, as step 1 says.
  • #19188: installing SecondWind doesn't connect it to Lifeline and MatchFlow. The install logs errors saying a component type was not found and a version is still being fetched. It writes Lifeline and MatchFlow under /source/deps/, but SecondWind's require("Lifeline.api") doesn't find them there: its components fail with module 'Lifeline.api' not found, and zm push refuses with could not resolve require("Lifeline.api"). Installing Lifeline and MatchFlow in a call of their own with at=/source, as step 1 says, fixes both. In the same call as SecondWind, or without at, that install answers updated with a path under /source/deps/ and puts nothing in /source. The copies under /source/deps/ stay behind; the MatchFlow one logs module ~.rules … has no root to resolve against on each scene rebuild, which the first example plays through.
  • #19168: effects spawned from a message another player sent. Worked around: the kit queues hit effects and draws them on the next frame instead of inside the message handler, and also draws them from the synced hitSeq/hitType/hitAmount fields when no message arrives.
  • #19201: the {PlayerName} label. The engine's humanoid avatar shows a {PlayerName} placeholder on any body no player owns, and has no setting to turn it off. This only matters if you give dummies or enemies the humanoid avatar. The showcase gallery hides those labels in play and in the editor: it disables PlayerNameLabel on bodies with no PlayerAvatar above them.
  • Message rate. Lifeline, which the kit builds on and does not change, sends one lifeline:evt message for every hit, so the owner of many bodies taking damage at once still sends at least one message per hit. A peer with a low frame rate, such as a browser tab at 10 to 15 fps, drains those messages slowly, and everything sent after them waits behind them. The kit's own events are batched (see 1.1.1). Keep the number of bodies taking damage at the same moment modest, or only run them near players, as the showcase gallery does.
  • Tower freeze, seen once in a browser. During a two-player test, a player in a web browser walked off the gallery's JUMP OFF platform and stayed hanging in the air, unable to move, until knocked out. It was not reproduced on the native engine, where the same walk falls and takes fall damage, and it may have come from the browser's controls. Nothing in the kit was found to cause it: the kit only moves a player's body when it respawns.
  • Two-player checks. Checked live with two players, one on the native engine and one in a web browser. In 1.1.0, hit effects, fall deaths and respawns matched on both screens. In 1.1.1, with the browser hosting the gallery, batched events arrived in order and damage requests came back in 0.2 to 0.4 s. The test suite also plays the remote peer's side (receiving, drifting, asking for a resync, catching up).
  • Web browser. The kit uses only cross-platform engine APIs (no file, OS or native calls outside its tests). In 1.1.1 a browser tab hosted the showcase gallery for the checks above and ran at about 15 frames a second with the gallery awake around a player, against 80 to 200 ms a frame before. Big scenes run slower in a browser than on the native engine, which is why the message rate above matters most there. The first example works in a browser: in a brand-new world with SecondWind installed by the two-call step 1 (on 1.1.2's code, which 1.1.3 and 1.1.4 don't change), the player started with 150 health, the lava set them on fire with flames, damage numbers and BURNING on screen, the bar dropped to about 20 of 150, and the dummy took damage. It plays choppily in a browser tab, even in that small scene. On the native engine the same scene runs at about 19 ms a frame, and the kit's components take about 0.5 ms of it; most of the rest is UI drawing, the built-in character controller and rendering, so the kit is not the likely cause. The browser frame itself has not been profiled. 1.1.4's example only moves the dummy and adds a plain coloured cube, and 1.2.0 only renames the package; both were checked on the native engine.

Comments · 0

Open discussion, no rating attached. Ask questions, swap notes, follow threads.

Sign in to join the discussion.