Entities & NPCs
Dokumentace k práci s entitami a NPC v Hytale.
Obsah
| Soubor | Popis |
|--------|-------|
| ENTITY_CREATION.md | Entity hierarchie, Holder pattern, komponenty, spawn |
| NPC_BEHAVIOR.md | NPCPlugin, Role, Instruction, Sensor, Motion, Action |
---
Entity Přehled
V Hytale jsou entity reprezentovány jako ECS entity s komponentami:
Entity = ID + KomponentyPříklad Player entity:
├── Player
├── PlayerRef
├── TransformComponent
├── HeadRotation
├── NetworkId
├── BoundingBox
└── ... další komponenty
---
Vytvoření Entity
Holder Pattern
public Holder createCustomEntity(Store store, Vector3d position) {
// Vytvoř holder pro novou entitu
Holder holder = EntityStore.REGISTRY.newHolder(); // Pozice a rotace
holder.addComponent(
TransformComponent.getComponentType(),
new TransformComponent(position, new Vector3f(0, 0, 0))
);
// Network ID (pro síťovou synchronizaci)
holder.addComponent(
NetworkId.getComponentType(),
new NetworkId(store.getExternalData().takeNextNetworkId())
);
// Bounding box
holder.addComponent(
BoundingBox.getComponentType(),
new BoundingBox(/ bounding box data /)
);
return holder;
}
Spawn do Světa
public void spawnEntity(World world, Holder holder) {
world.execute(() -> {
Store store = world.getEntityStore().getStore();
store.addEntity(holder, AddReason.SPAWN);
});
}
---
Entity s Modelem
Z TeleportPlugin - vytvoření warp markeru:
@Nonnull
public Holder createWarp(@Nonnull Warp warp, @Nonnull Store store) {
Transform transform = warp.getTransform();
Holder holder = EntityStore.REGISTRY.newHolder(); // Pozice
holder.addComponent(
TransformComponent.getComponentType(),
new TransformComponent(transform.getPosition(), transform.getRotation())
);
// Network ID
holder.addComponent(
NetworkId.getComponentType(),
new NetworkId(store.getExternalData().takeNextNetworkId())
);
// Nelze interagovat
holder.ensureComponent(Intangible.getComponentType());
// Model
holder.addComponent(
ModelComponent.getComponentType(),
new ModelComponent(this.warpModel)
);
// Bounding box z modelu
holder.addComponent(
BoundingBox.getComponentType(),
new BoundingBox(this.warpModel.getBoundingBox())
);
// Jmenovka
holder.addComponent(
Nameplate.getComponentType(),
new Nameplate(warp.getId())
);
// Skrytá v adventure módu
holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType());
// Neserializovat
holder.ensureComponent(EntityStore.REGISTRY.getNonSerializedComponentType());
return holder;
}
---
Entity Komponenty
Základní Komponenty
| Komponenta | Popis |
|------------|-------|
| TransformComponent | Pozice a rotace |
| NetworkId | ID pro síťovou synchronizaci |
| BoundingBox | Kolizní box |
| ModelComponent | 3D model |
| Nameplate | Jmenovka nad entitou |
Speciální Komponenty
| Komponenta | Popis |
|------------|-------|
| Intangible | Nelze s entitou interagovat |
| HiddenFromAdventurePlayers | Skrytá pro adventure hráče |
| MovementStatesComponent | Stav pohybu |
| EffectControllerComponent | Aktivní efekty |
| ProjectileComponent | Projektil |
---
NPC Definice (Assets)
NPC jsou definovány v Assets/Server/NPC/:
{
"Id": "merchant",
"Name": "Merchant",
"Model": "npc_merchant",
"Health": 100,
"Behavior": {
"Type": "Passive",
"Interactions": [
{
"Type": "Trade",
"TradeList": "merchant_trades"
}
]
},
"Dialogue": {
"Greeting": "dialogue.merchant.greeting",
"Farewell": "dialogue.merchant.farewell"
}
}
---
Entity Definice (Assets)
Entity v Assets/Server/Entity/:
{
"Id": "zombie",
"Name": "Zombie",
"Model": "mob_zombie",
"Health": 20,
"Damage": 3,
"MovementSpeed": 0.23,
"Behavior": {
"Type": "Hostile",
"TargetSelector": "NearestPlayer",
"AttackRange": 2.0
},
"Drops": "zombie_drops",
"SpawnConditions": {
"LightLevel": { "Max": 7 },
"Biomes": ["plains", "forest", "desert"]
}
}
---
Spawn při Načtení Chunku
private void onChunkPreLoadProcess(ChunkPreLoadProcessEvent event) {
WorldChunk chunk = event.getChunk();
BlockChunk blockChunk = chunk.getBlockChunk(); if (blockChunk == null) return;
int chunkX = blockChunk.getX();
int chunkZ = blockChunk.getZ();
World world = chunk.getWorld();
// Zkontroluj jestli má tento chunk naše entity
for (MyEntity entity : getEntitiesForChunk(chunkX, chunkZ)) {
Vector3d position = entity.getPosition();
// Ověř že pozice je v tomto chunku
if (ChunkUtil.isInsideChunk(chunkX, chunkZ,
MathUtil.floor(position.x),
MathUtil.floor(position.z))) {
// Spawn na world threadu
world.execute(() -> {
Store store = world.getEntityStore().getStore();
Holder holder = createEntityHolder(entity, store);
store.addEntity(holder, AddReason.LOAD);
});
}
}
}
---
EntityRemoveEvent
public class EntityRemoveSystem extends EntityEventSystem { public EntityRemoveSystem() {
super(EntityRemoveEvent.class);
}
@Override
public void handle(int i, ArchetypeChunk chunk,
Store store, CommandBuffer buffer,
EntityRemoveEvent event) {
Ref ref = chunk.getReferenceTo(i);
// Entita je odstraňována ze světa
getLogger().atInfo().log("Entity removed");
}
@Override
public Query getQuery() {
return null; // Všechny entity
}
}
---
Práce s Existujícími Entitami
Získání Entity Reference
// Z eventu
Ref ref = chunk.getReferenceTo(i);// Z hráče
Ref playerRef = player.getRef();
// Přes store
// (závisí na konkrétním API)
Modifikace Entity
world.execute(() -> {
Ref ref = entity.getRef();
Store store = ref.getStore(); // Změna pozice
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (transform != null) {
transform.setPosition(newPosition);
store.setComponent(ref, TransformComponent.getComponentType(), transform);
}
// Přidání komponenty
store.addComponent(ref, MyComponent.getComponentType(), new MyComponent());
// Odebrání komponenty
store.removeComponent(ref, SomeComponent.getComponentType());
});
Zničení Entity
// Přes CommandBuffer (v systému)
commandBuffer.destroyEntity(ref);// Nebo přes store
// store.removeEntity(ref);
---
UUID a Identifikace
UUIDComponent
// Získání UUID entity
UUIDComponent uuidComp = store.getComponent(ref, UUIDComponent.getComponentType());
if (uuidComp != null) {
UUID entityUuid = uuidComp.getUuid();
}
NetworkId
// Pro síťovou identifikaci
NetworkId networkId = store.getComponent(ref, NetworkId.getComponentType());
int id = networkId.getId();
---
Shrnutí
| Operace | Metoda |
|---------|--------|
| Vytvořit holder | EntityStore.REGISTRY.newHolder() |
| Přidat komponentu | holder.addComponent(type, component) |
| Zajistit komponentu | holder.ensureComponent(type) |
| Spawn entity | store.addEntity(holder, AddReason.SPAWN) |
| Získat network ID | store.getExternalData().takeNextNetworkId() |
| Zničit entity | commandBuffer.destroyEntity(ref) |
| AddReason | Použití |
|-----------|---------|
| SPAWN | Nově vytvořená entita |
| LOAD | Načtená entita (z chunku) |