hytale-ecs
4
总安装量
3
周安装量
#54416
全站排名
安装命令
npx skills add https://github.com/z3nlotus/hytale-agent-skills --skill hytale-ecs
Agent 安装分布
codex
3
github-copilot
3
gemini-cli
3
opencode
2
cursor
2
claude-code
2
Skill 文档
Hytale Entity Component System (ECS)
Master Hytale’s ECS architecture for performant game mechanics.
What is ECS?
ECS is an architectural pattern that separates:
- Entity: A unique identifier (just an ID)
- Component: Pure data (no logic)
- System: Pure logic (no data)
This enables:
- â Better performance (cache-friendly)
- â Easier composition (mix and match)
- â Cleaner code (separation of concerns)
Core Concepts
Entity
An entity is just an ID – a number that groups components together.
// Entity has no data or behavior itself
Entity player = world.createEntity();
Entity monster = world.createEntity();
Component
Components are data containers – they describe what an entity “has”.
// Components are pure data
public record PositionComponent(float x, float y, float z) {}
public record HealthComponent(float current, float max) {}
public record VelocityComponent(float vx, float vy, float vz) {}
public record NameComponent(String name) {}
System
Systems contain logic – they operate on entities with specific components.
// Systems process entities with required components
public class MovementSystem implements System {
@Override
public void update(World world, float deltaTime) {
// Query all entities with Position AND Velocity
world.query(PositionComponent.class, VelocityComponent.class)
.forEach((entity, pos, vel) -> {
// Update position based on velocity
entity.setComponent(new PositionComponent(
pos.x() + vel.vx() * deltaTime,
pos.y() + vel.vy() * deltaTime,
pos.z() + vel.vz() * deltaTime
));
});
}
}
Composition Over Inheritance
â Traditional OOP (Inheritance)
Entity
â
ââââââ´âââââ
â â
Character Item
â
âââââ´ââââ
â â
Player NPC
Problem: What if NPC needs inventory like Player?
â ECS (Composition)
Player Entity:
+ PositionComponent
+ HealthComponent
+ InventoryComponent
+ PlayerControllerComponent
NPC Entity:
+ PositionComponent
+ HealthComponent
+ InventoryComponent â Easy to add!
+ AIControllerComponent
Item Entity:
+ PositionComponent
+ ItemDataComponent
Working with Components
Adding Components
Entity player = world.createEntity();
player.addComponent(new PositionComponent(0, 64, 0));
player.addComponent(new HealthComponent(100, 100));
player.addComponent(new InventoryComponent());
Getting Components
// Get single component
var health = entity.getComponent(HealthComponent.class);
if (health != null) {
float current = health.current();
}
// Check if has component
if (entity.hasComponent(FlyingComponent.class)) {
// Handle flying entity
}
// Optional API
entity.getComponentOptional(HealthComponent.class)
.ifPresent(h -> h.heal(10));
Removing Components
// Remove a component
entity.removeComponent(FlyingComponent.class);
// Entity becomes "different" without that component
// Systems that require FlyingComponent will skip it
Entity Queries
Query entities based on their components:
// All entities with Health
world.query(HealthComponent.class)
.forEach((entity, health) -> {
if (health.current() <= 0) {
entity.destroy();
}
});
// All entities with Position AND Velocity AND NOT Flying
world.query()
.with(PositionComponent.class)
.with(VelocityComponent.class)
.without(FlyingComponent.class)
.forEach((entity, pos, vel) -> {
// Apply gravity
});
Common Component Patterns
Transform Components
public record PositionComponent(float x, float y, float z) {}
public record RotationComponent(float pitch, float yaw, float roll) {}
public record ScaleComponent(float x, float y, float z) {}
Gameplay Components
public record HealthComponent(float current, float max) {
public boolean isDead() { return current <= 0; }
}
public record DamageComponent(float amount, Entity source) {}
public record InventoryComponent(List<ItemStack> items) {}
AI Components
public record AITargetComponent(Entity target) {}
public record PatrolRouteComponent(List<Position> waypoints) {}
public record AggroRangeComponent(float range) {}
Tags (Empty Components)
// Tag components have no data
public record PlayerTag() {}
public record HostileTag() {}
public record InvulnerableTag() {}
// Use for filtering
world.query(PlayerTag.class, HealthComponent.class)
.forEach((entity, _, health) -> {
// Only players with health
});
System Design
System Interface
public interface System {
void update(World world, float deltaTime);
default int priority() { return 0; } // Lower = runs first
}
Example Systems
public class GravitySystem implements System {
@Override
public void update(World world, float deltaTime) {
world.query(VelocityComponent.class)
.without(FlyingComponent.class)
.forEach((entity, vel) -> {
entity.setComponent(new VelocityComponent(
vel.vx(),
vel.vy() - 9.8f * deltaTime, // Apply gravity
vel.vz()
));
});
}
@Override
public int priority() { return 10; } // Run early
}
public class DamageSystem implements System {
@Override
public void update(World world, float deltaTime) {
world.query(HealthComponent.class, DamageComponent.class)
.forEach((entity, health, damage) -> {
float newHealth = health.current() - damage.amount();
entity.setComponent(new HealthComponent(newHealth, health.max()));
entity.removeComponent(DamageComponent.class);
});
}
}
Best Practices
Do
| Practice | Why |
|---|---|
| Keep components small | Better cache usage |
| Use records for components | Immutable, simple |
| Prefer composition | Flexible, reusable |
| Use tag components | Clear intent |
| Query efficiently | Only needed components |
Don’t
| Anti-pattern | Why Bad |
|---|---|
| Logic in components | Breaks ECS pattern |
| Giant components | Poor performance |
| Entity knowing systems | Tight coupling |
| Inheritance hierarchies | Defeats composition |
Quick Reference
| Concept | What It Is |
|---|---|
| Entity | Just an ID |
| Component | Pure data |
| System | Pure logic |
| Query | Find entities by components |
| Tag | Empty component for filtering |
Resources
- Hytale API: See
hytale-plugin-devskill - Java Features: See
java-25-hytalefor records/patterns