Models
Detailní dokumentace k práci s 3D modely v Hytale.
---
Přehled Architektury
Hytale používá hierarchii tříd pro modely:
ModelAsset (Asset definice v JSON)
↓
Model (Runtime instance s scale, attachments, atd.)
↓
ModelComponent (ECS komponenta pro entity)
---
Model Třída
Z dekompilovaného kódu - hlavní runtime třída pro modely:
public class Model implements NetworkSerializable {
public static final String UNKNOWN_TEXTURE = "textures/Unknown.png"; // Identifikace
private final String modelAssetId;
// Vizuální vlastnosti
private final String model; // Cesta k .blockymodel souboru
private final String texture; // Cesta k textuře
private final String gradientSet; // Gradient set ID
private final String gradientId; // Gradient ID
// Škálování
private final float scale;
// Fyzika a kolize
@Nullable
private final Box boundingBox;
@Nullable
private final Box crouchBoundingBox;
private final PhysicsValues physicsValues;
private final Map detailBoxes;
// Pohled postavy
private final float eyeHeight;
private final float crouchOffset;
private final CameraSettings camera;
// Animace
private final Map animationSetMap;
// Příslušenství
private final ModelAttachment[] attachments;
private final Map randomAttachmentIds;
// Efekty
private final ColorLight light;
private final ModelParticle[] particles;
private final ModelTrail[] trails;
// Fobie (alternativní model pro hráče s fobiemi)
private final Phobia phobia;
private final String phobiaModelAssetId;
// Cache pro síťový packet
private transient SoftReference cachedPacket;
}
Klíčové Vlastnosti
| Vlastnost | Typ | Popis |
|-----------|-----|-------|
| modelAssetId | String | ID assetu v registru |
| model | String | Cesta k .blockymodel souboru |
| texture | String | Cesta k textuře |
| scale | float | Škála modelu |
| boundingBox | Box | Kolizní box |
| eyeHeight | float | Výška očí postavy |
| crouchOffset | float | Offset při dřepu |
| attachments | ModelAttachment[] | Příslušenství modelu |
| animationSetMap | Map | Sety animací |
| particles | ModelParticle[] | Částicové efekty |
| trails | ModelTrail[] | Trail efekty |
---
Vytváření Modelů
Factory Metody
// Unit scale (1:1)
Model model = Model.createUnitScaleModel(modelAsset);// Unit scale s vlastním bounding boxem
Model model = Model.createUnitScaleModel(modelAsset, customBoundingBox);
// Náhodná škála (z rozsahu definovaného v assetu)
Model model = Model.createRandomScaleModel(modelAsset);
// Konkrétní škála
Model model = Model.createScaledModel(modelAsset, 1.5f);
// Škála s náhodnými attachmenty
Map randomAttachments = modelAsset.generateRandomAttachmentIds();
Model model = Model.createScaledModel(modelAsset, 1.0f, randomAttachments);
// Statický model (bez animací - pro props)
Model model = Model.createStaticScaledModel(modelAsset, 1.0f);
Škálování
Při vytváření modelu s jinou škálou než 1.0 se automaticky škálují:
// Z createScaledModel():
if (scale != 1.0F) {
boundingBox = boundingBox.clone().scale(scale);
eyeHeight *= scale;
crouchOffset *= scale; if (camera != null) {
camera = camera.clone().scale(scale);
}
if (physicsValues != null) {
physicsValues = new PhysicsValues(physicsValues);
physicsValues.scale(scale);
}
// Škálování particles a trails...
}
---
ModelAsset
Asset definice načtená z JSON:
// Získání assetu z registru
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset("Player");// Kontrola existence
if (modelAsset == null) {
modelAsset = ModelAsset.DEBUG; // Fallback na debug model
}
// Generování náhodných attachmentů
Map randomAttachments = modelAsset.generateRandomAttachmentIds();
// Generování náhodné škály
float scale = modelAsset.generateRandomScale();
Asset Vlastnosti
public class ModelAsset {
// Základní
public String getId();
public String getModel();
public String getTexture(); // Fyzika
public Box getBoundingBox();
public float getEyeHeight();
public float getCrouchOffset();
public PhysicsValues getPhysicsValues();
// Vizuální
public String getGradientSet();
public String getGradientId();
public ColorLight getLight();
public CameraSettings getCamera();
// Animace
public Map getAnimationSetMap();
// Efekty
public ModelParticle[] getParticles();
public ModelTrail[] getTrails();
// Attachmenty
public ModelAttachment[] getAttachments(Map randomAttachmentIds);
// Fobie
public Phobia getPhobia();
public String getPhobiaModelAssetId();
}
---
ModelComponent
ECS komponenta pro přiřazení modelu k entitě:
public class ModelComponent implements Component {
private final Model model;
private boolean isNetworkOutdated = true; // Získání ComponentType
public static ComponentType getComponentType() {
return EntityModule.get().getModelComponentType();
}
public ModelComponent(Model model) {
this.model = model;
}
public Model getModel() {
return this.model;
}
// Pro síťovou synchronizaci
public boolean consumeNetworkOutdated() {
boolean temp = this.isNetworkOutdated;
this.isNetworkOutdated = false;
return temp;
}
@Override
public Component clone() {
return new ModelComponent(this.model);
}
}
Přidání k Entitě
// Vytvoření entity s modelem
Holder holder = EntityStore.REGISTRY.newHolder();// Přidání pozice
holder.addComponent(
TransformComponent.getComponentType(),
new TransformComponent(position, rotation)
);
// Přidání modelu
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset("MyModel");
Model model = Model.createUnitScaleModel(modelAsset);
holder.addComponent(
ModelComponent.getComponentType(),
new ModelComponent(model)
);
// Přidání bounding boxu z modelu
if (model.getBoundingBox() != null) {
holder.addComponent(
BoundingBox.getComponentType(),
new BoundingBox(model.getBoundingBox())
);
}
// Spawn do světa
store.addEntity(holder, AddReason.SPAWN);
---
ModelAttachment
Příslušenství modelu (zbraně, oblečení, atd.):
public class ModelAttachment implements NetworkSerializable<...> {
public static final BuilderCodec CODEC; protected String model; // Cesta k attachment modelu
protected String texture; // Textura
protected String gradientSet; // Gradient set
protected String gradientId; // Gradient ID
protected double weight = 1.0; // Váha pro náhodný výběr
public ModelAttachment(String model, String texture,
String gradientSet, String gradientId,
double weight) {
this.model = model;
this.texture = texture;
this.gradientSet = gradientSet;
this.gradientId = gradientId;
this.weight = weight;
}
}
JSON Definice Attachmentu
{
"Attachments": {
"helmet": [
{
"Model": "models/armor/helmet_iron.blockymodel",
"Texture": "textures/armor/helmet_iron.png",
"Weight": 1.0
},
{
"Model": "models/armor/helmet_gold.blockymodel",
"Texture": "textures/armor/helmet_gold.png",
"Weight": 0.5
}
]
}
}
---
Model.ModelReference
Pro serializaci/deserializaci modelu:
public static class ModelReference {
public static final BuilderCodec CODEC;
public static final ModelReference DEFAULT_PLAYER_MODEL =
new ModelReference("Player", -1.0F, null, false); private String modelAssetId;
private float scale;
private Map randomAttachmentIds;
private boolean staticModel;
// Konverze zpět na Model
@Nullable
public Model toModel() {
if (this.modelAssetId == null) {
return null;
}
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(this.modelAssetId);
if (modelAsset == null) {
modelAsset = ModelAsset.DEBUG;
}
return Model.createScaledModel(modelAsset, this.scale,
this.randomAttachmentIds, null, this.staticModel);
}
}
Použití Reference
// Model na referenci
Model model = ...;
Model.ModelReference reference = model.toReference();// Reference zpět na model
Model recreatedModel = reference.toModel();
---
PersistentModel Komponenta
Pro perzistenci modelu entity:
// Entity s PersistentModel se automaticky načtou se správným modelem
holder.addComponent(
PersistentModel.getComponentType(),
new PersistentModel(model.toReference())
);
Automatické Systémy
// SetRenderedModel - vytvoří ModelComponent z PersistentModel při loadu
public static class SetRenderedModel extends HolderSystem {
@Override
public void onEntityAdd(Holder holder, AddReason reason, Store store) {
PersistentModel persistentModel = holder.getComponent(persistentModelComponentType);
holder.putComponent(modelComponentType,
new ModelComponent(persistentModel.getModelReference().toModel()));
}
}// ModelChange - aktualizuje PersistentModel při změně ModelComponent
public static class ModelChange extends RefChangeSystem {
public void onComponentSet(...) {
PersistentModel persistentModel = store.getComponent(ref, persistentModelComponentType);
persistentModel.setModelReference(newComponent.getModel().toReference());
}
}
---
Model Systems
ECS systémy pro práci s modely:
ModelSpawned
Automaticky nastaví BoundingBox při spawnu entity s modelem:
public static class ModelSpawned extends HolderSystem {
@Override
public void onEntityAdd(Holder holder, AddReason reason, Store store) {
Model model = holder.getComponent(modelComponentType).getModel();
Box modelBoundingBox = model.getBoundingBox(); if (modelBoundingBox != null) {
BoundingBox boundingBox = holder.getComponent(boundingBoxComponentType);
if (boundingBox == null) {
boundingBox = new BoundingBox();
holder.addComponent(boundingBoxComponentType, boundingBox);
}
boundingBox.setBoundingBox(modelBoundingBox);
boundingBox.setDetailBoxes(model.getDetailBoxes());
}
}
}
PlayerConnect
Nastaví model hráči při připojení:
public static class PlayerConnect extends HolderSystem {
@Override
public void onEntityAdd(Holder holder, AddReason reason, Store store) {
Player player = holder.getComponent(playerComponentType);
String preset = player.getPlayerConfigData().getPreset(); ModelAsset modelAsset = preset != null
? ModelAsset.getAssetMap().getAsset(preset)
: null;
if (modelAsset != null) {
Model model = Model.createUnitScaleModel(modelAsset);
holder.addComponent(modelComponentType, new ModelComponent(model));
} else {
// Fallback na default Player model
ModelAsset defaultModelAsset = ModelAsset.getAssetMap().getAsset("Player");
if (defaultModelAsset != null) {
Model defaultModel = Model.createUnitScaleModel(defaultModelAsset);
holder.addComponent(modelComponentType, new ModelComponent(defaultModel));
}
}
}
}
UpdateBoundingBox
Aktualizuje bounding box při změně modelu:
public static class UpdateBoundingBox extends RefChangeSystem {
public void onComponentSet(...) {
BoundingBox boundingBox = commandBuffer.getComponent(ref, boundingBoxComponentType);
MovementStatesComponent movementStates = commandBuffer.getComponent(ref, movementStatesComponentType);
updateBoundingBox(newComponent.getModel(), boundingBox, movementStates);
} protected static void updateBoundingBox(Model model, BoundingBox boundingBox, MovementStates movementStates) {
Box modelBoundingBox = model.getBoundingBox(movementStates);
if (modelBoundingBox == null) {
modelBoundingBox = new Box();
}
boundingBox.setBoundingBox(modelBoundingBox);
}
}
---
Bounding Box a Crouch
Model podporuje různé bounding boxy pro normální stav a dřep:
// Z Model konstruktoru:
this.crouchBoundingBox = boundingBox == null
? null
: new Box(boundingBox.min.clone(),
boundingBox.max.clone().add(0.0, (double)crouchOffset, 0.0));// Získání správného bounding boxu:
@Nullable
public Box getBoundingBox(@Nullable MovementStates movementStates) {
if (movementStates == null) {
return this.boundingBox;
}
return !movementStates.crouching && !movementStates.forcedCrouching
? this.boundingBox
: this.crouchBoundingBox;
}
---
Animace
Získání Animace
Model model = ...;// Získání první dostupné animace z preference
String animId = model.getFirstBoundAnimationId("walk", "idle", "default");
// Získání animation set mapy
Map animations = model.getAnimationSetMap();
Animation Sets v JSON
{
"AnimationSets": {
"idle": {
"Animation": "animations/idle.animation",
"Loop": true
},
"walk": {
"Animation": "animations/walk.animation",
"Loop": true,
"BlendTime": 0.2
},
"attack": {
"Animation": "animations/attack.animation",
"Loop": false
}
}
}
---
LoadedAssetsEvent
Reagování na načtení modelů:
@Override
protected void setup() {
getEventRegistry().register(LoadedAssetsEvent.class, ModelAsset.class, event -> {
Map loadedModels = event.getLoadedAssets(); ModelAsset myModel = loadedModels.get("MyCustomModel");
if (myModel != null) {
// Model byl načten nebo změněn - invaliduj cache
this.cachedModel = null;
}
});
}
---
Shrnutí
| Třída | Účel |
|-------|------|
| Model | Runtime instance modelu |
| ModelAsset | Asset definice z JSON |
| ModelComponent | ECS komponenta |
| ModelAttachment | Příslušenství modelu |
| Model.ModelReference | Pro serializaci |
| PersistentModel | Pro perzistenci |
| Factory Metoda | Účel |
|----------------|------|
| createUnitScaleModel() | Model se škálou 1:1 |
| createRandomScaleModel() | Náhodná škála z rozsahu |
| createScaledModel() | Konkrétní škála |
| createStaticScaledModel() | Bez animací (props) |
| Systém | Kdy |
|--------|-----|
| SetRenderedModel | Load entity - vytvoří ModelComponent |
| ModelSpawned | Spawn - nastaví BoundingBox |
| PlayerConnect | Připojení hráče - nastaví model |
| UpdateBoundingBox | Změna modelu - aktualizuje kolize |
| ModelChange | Změna modelu - aktualizuje PersistentModel |