hytale-custom-assets
Creating Custom Hytale Assets
Complete guide for creating and managing custom assets including models, textures, sounds, and particles.
When to use this skill
Use this skill when:
- Creating asset packs for plugins
- Adding custom textures and models
- Configuring sounds and music
- Creating particle effects
- Setting up animations
- Managing asset inheritance and tags
Asset System Architecture
Hytale uses a hierarchical asset system:
AssetPack
├── Server/ # Server-side assets
│ └── Content/ # Game content definitions
│ ├── BlockTypes/
│ ├── Items/
│ ├── Entities/
│ └── ...
└── Client/ # Client-side assets
├── Models/
├── Textures/
├── Sounds/
├── Particles/
└── ...
Asset Pack Structure
my-plugin/
└── assets/
├── pack.json # Pack metadata
├── Server/
│ └── Content/
│ ├── BlockTypes/
│ │ └── custom_block.blocktype
│ ├── Items/
│ │ └── custom_item.item
│ ├── Entities/
│ │ └── custom_entity.entity
│ ├── Recipes/
│ │ └── custom_recipe.recipe
│ └── Sounds/
│ └── custom_soundset.soundset
└── Client/
├── Models/
│ └── custom_model.model
├── Textures/
│ ├── blocks/
│ │ └── custom_block.png
│ ├── items/
│ │ └── custom_item.png
│ └── entities/
│ └── custom_entity.png
├── Sounds/
│ └── custom/
│ └── sound.ogg
├── Particles/
│ └── custom_particle.particle
└── Animations/
└── custom_animation.animation
Pack Metadata
File: pack.json
{
"Name": "MyPlugin Assets",
"Id": "MyPlugin",
"Version": "1.0.0",
"Description": "Custom assets for MyPlugin",
"Author": "Your Name",
"Priority": 100,
"Dependencies": ["Hytale:Core"]
}
Pack Properties
| Property | Type | Description |
|---|---|---|
Name |
String | Display name |
Id |
String | Unique identifier |
Version |
String | Semantic version |
Description |
String | Pack description |
Author |
String | Creator name |
Priority |
Integer | Load order (higher = later) |
Dependencies |
Array | Required packs |
Textures
Texture Formats
| Format | Use Case |
|---|---|
| PNG | Standard textures (recommended) |
| TGA | Textures with alpha |
| DDS | Compressed textures |
Block Textures
Location: Client/Textures/blocks/
custom_block.png # All faces
custom_block_top.png # Top face only
custom_block_side.png # Side faces
custom_block_bottom.png # Bottom face
Texture Size: 16x16, 32x32, 64x64 (power of 2)
Item Textures
Location: Client/Textures/items/
custom_item.png # Inventory icon (32x32)
custom_item_held.png # Held model texture
Entity Textures
Location: Client/Textures/entities/
custom_entity.png # Main texture atlas
custom_entity_glow.png # Emissive layer
custom_entity_normal.png # Normal map
Models
Model Format
Hytale uses a custom JSON-based model format.
File: custom_model.model
{
"Parent": "Hytale/Models/base_entity",
"Textures": {
"main": "MyPlugin/Textures/entities/custom_entity"
},
"Bones": [
{
"Name": "root",
"Pivot": [0, 0, 0],
"Children": [
{
"Name": "body",
"Pivot": [0, 12, 0],
"Cubes": [
{
"Origin": [-4, 0, -2],
"Size": [8, 12, 4],
"UV": [0, 0]
}
],
"Children": [
{
"Name": "head",
"Pivot": [0, 24, 0],
"Cubes": [
{
"Origin": [-4, 0, -4],
"Size": [8, 8, 8],
"UV": [0, 16]
}
]
}
]
}
]
}
],
"Animations": {
"idle": "MyPlugin/Animations/custom_idle",
"walk": "MyPlugin/Animations/custom_walk"
}
}
Model Properties
| Property | Type | Description |
|---|---|---|
Parent |
String | Parent model to inherit |
Textures |
Object | Texture bindings |
Bones |
Array | Bone hierarchy |
Animations |
Object | Animation bindings |
Scale |
Float | Overall scale |
TextureSize |
Array | Texture dimensions [W, H] |
Bone Properties
| Property | Type | Description |
|---|---|---|
Name |
String | Bone identifier |
Pivot |
Array | Rotation pivot point |
Rotation |
Array | Default rotation [X, Y, Z] |
Cubes |
Array | Geometry cubes |
Children |
Array | Child bones |
Mirror |
Boolean | Mirror UV horizontally |
Cube Properties
| Property | Type | Description |
|---|---|---|
Origin |
Array | Position offset |
Size |
Array | Dimensions [W, H, D] |
UV |
Array | Texture UV offset [U, V] |
Inflate |
Float | Expand cube size |
Mirror |
Boolean | Mirror this cube |
Animations
File: custom_animation.animation
{
"Length": 1.0,
"Loop": true,
"Bones": {
"body": {
"Rotation": {
"0.0": [0, 0, 0],
"0.5": [5, 0, 0],
"1.0": [0, 0, 0]
}
},
"leftArm": {
"Rotation": {
"0.0": [0, 0, 0],
"0.25": [-30, 0, 0],
"0.5": [0, 0, 0],
"0.75": [30, 0, 0],
"1.0": [0, 0, 0]
}
},
"rightArm": {
"Rotation": {
"0.0": [0, 0, 0],
"0.25": [30, 0, 0],
"0.5": [0, 0, 0],
"0.75": [-30, 0, 0],
"1.0": [0, 0, 0]
}
}
}
}
Animation Properties
| Property | Type | Description |
|---|---|---|
Length |
Float | Duration in seconds |
Loop |
Boolean | Loop animation |
Bones |
Object | Per-bone keyframes |
Keyframe Channels
| Channel | Type | Description |
|---|---|---|
Rotation |
Array | Rotation [X, Y, Z] degrees |
Position |
Array | Position offset [X, Y, Z] |
Scale |
Array | Scale [X, Y, Z] |
Sounds
Sound Formats
| Format | Use Case |
|---|---|
| OGG | Music, ambient (recommended) |
| WAV | Short sound effects |
Sound Event
File: custom_sound.soundevent
{
"Sounds": [
{
"Path": "MyPlugin/Sounds/custom/sound1",
"Weight": 1.0
},
{
"Path": "MyPlugin/Sounds/custom/sound2",
"Weight": 0.5
}
],
"Volume": {
"Min": 0.8,
"Max": 1.0
},
"Pitch": {
"Min": 0.9,
"Max": 1.1
},
"Category": "Effects",
"Subtitle": {
"en-US": "Custom sound plays"
}
}
Sound Set
File: custom_soundset.soundset
{
"Sounds": {
"break": "MyPlugin/SoundEvents/custom_break",
"place": "MyPlugin/SoundEvents/custom_place",
"step": "MyPlugin/SoundEvents/custom_step",
"hit": "MyPlugin/SoundEvents/custom_hit"
}
}
Block Sound Set
File: custom_blocksound.blocksound
{
"Break": {
"Sound": "MyPlugin/SoundEvents/metal_break",
"Volume": 1.0,
"Pitch": 1.0
},
"Place": {
"Sound": "MyPlugin/SoundEvents/metal_place",
"Volume": 1.0
},
"Step": {
"Sound": "MyPlugin/SoundEvents/metal_step",
"Volume": 0.5
},
"Fall": {
"Sound": "MyPlugin/SoundEvents/metal_fall"
}
}
Sound Categories
| Category | Description |
|---|---|
Master |
All sounds |
Music |
Background music |
Effects |
Sound effects |
Ambient |
Environmental sounds |
Voice |
Voice/dialogue |
UI |
Interface sounds |
Weather |
Weather sounds |
Particles
File: custom_particle.particle
{
"Texture": "MyPlugin/Textures/particles/sparkle",
"MaxParticles": 100,
"EmissionRate": 10,
"Lifetime": {
"Min": 0.5,
"Max": 1.5
},
"Size": {
"Start": 0.2,
"End": 0.0
},
"Color": {
"Start": { "R": 1.0, "G": 0.8, "B": 0.2, "A": 1.0 },
"End": { "R": 1.0, "G": 0.2, "B": 0.0, "A": 0.0 }
},
"Velocity": {
"Min": [-0.5, 1.0, -0.5],
"Max": [0.5, 2.0, 0.5]
},
"Gravity": -0.5,
"Collision": false,
"BlendMode": "Additive"
}
Particle Properties
| Property | Type | Description |
|---|---|---|
Texture |
String | Particle texture |
MaxParticles |
Integer | Maximum active particles |
EmissionRate |
Float | Particles per second |
Lifetime |
Range | Particle lifespan |
Size |
Range/Gradient | Size over lifetime |
Color |
Color/Gradient | Color over lifetime |
Velocity |
Range | Initial velocity |
Gravity |
Float | Gravity effect |
Collision |
Boolean | Collide with blocks |
BlendMode |
Enum | Alpha, Additive, Multiply |
Block Particle
File: custom_blockparticle.blockparticle
{
"Break": {
"Particle": "MyPlugin/Particles/shatter",
"Count": 30
},
"Step": {
"Particle": "MyPlugin/Particles/dust",
"Count": 3
},
"Ambient": {
"Particle": "MyPlugin/Particles/glow",
"Rate": 1,
"Radius": 0.5
}
}
Trails
File: custom_trail.trail
{
"Texture": "MyPlugin/Textures/trails/slash",
"Width": 0.5,
"Length": 10,
"Lifetime": 0.3,
"Color": {
"Start": { "R": 1.0, "G": 1.0, "B": 1.0, "A": 1.0 },
"End": { "R": 1.0, "G": 1.0, "B": 1.0, "A": 0.0 }
},
"BlendMode": "Additive",
"AttachPoint": "weapon_tip"
}
Asset Inheritance
Assets can inherit from parents:
{
"Parent": "Hytale:Stone",
"DisplayName": { "en-US": "Enchanted Stone" },
"LightLevel": 5
}
Inheritance Rules
- Child inherits all parent properties
- Child can override any property
- Nested objects merge (not replace)
- Arrays replace (not merge)
Asset Tags
Categorize assets with tags:
{
"Tags": {
"Category": ["Building", "Decorative"],
"Material": ["Stone"],
"Origin": ["Natural", "Cave"]
}
}
Tag Queries
Used in spawning, loot tables, etc.:
{
"Blocks": {
"MatchAll": ["Material:Stone"],
"MatchAny": ["Category:Natural", "Origin:Cave"],
"Exclude": ["Category:Artificial"]
}
}
Localization
File: lang/en-US.json
{
"item.myPlugin.customItem.name": "Custom Item",
"item.myPlugin.customItem.description": "A special custom item",
"block.myPlugin.customBlock.name": "Custom Block",
"entity.myPlugin.customEntity.name": "Custom Creature"
}
Localization Keys
| Pattern | Example |
|---|---|
item.{plugin}.{id}.name |
item.myPlugin.sword.name |
block.{plugin}.{id}.name |
block.myPlugin.ore.name |
entity.{plugin}.{id}.name |
entity.myPlugin.mob.name |
ui.{plugin}.{key} |
ui.myPlugin.menuTitle |
Registering Assets in Plugin
@Override
protected void setup() {
// Asset pack is auto-registered if manifest.json has:
// "IncludesAssetPack": true
// Listen for asset loading
getEventRegistry().register(
LoadedAssetsEvent.class,
BlockType.class,
this::onBlockTypesLoaded
);
getEventRegistry().register(
LoadedAssetsEvent.class,
Item.class,
this::onItemsLoaded
);
}
private void onBlockTypesLoaded(LoadedAssetsEvent<BlockType> event) {
// Access loaded block types
for (BlockType block : event.getLoadedAssets()) {
getLogger().atInfo().log("Loaded block: %s", block.getId());
}
}
Hot Reloading
Assets support hot-reload during development:
/reload assets
Listen for reload events:
getEventRegistry().register(
AssetStoreMonitorEvent.class,
this::onAssetReload
);
private void onAssetReload(AssetStoreMonitorEvent event) {
if (event.getAssetStore().getAssetClass() == BlockType.class) {
getLogger().atInfo().log("Block types reloaded");
// Re-initialize dependent systems
}
}
Custom Asset Store
Register entirely new asset types:
public class MyCustomAsset implements JsonAssetWithMap<String, DefaultAssetMap<String, MyCustomAsset>> {
public static final AssetBuilderCodec<String, MyCustomAsset> CODEC =
AssetBuilderCodec.builder(MyCustomAsset.class, "MyCustomAsset")
.appendRequired(Codec.STRING.fieldOf("Name"), MyCustomAsset::getName, (a, v) -> a.name = v)
.appendOptional(Codec.INT.fieldOf("Value"), MyCustomAsset::getValue, (a, v) -> a.value = v, 0)
.build();
private String id;
private String name;
private int value;
@Override
public String getId() { return id; }
public String getName() { return name; }
public int getValue() { return value; }
}
// Register in plugin
@Override
protected void setup() {
HytaleAssetStore<String, MyCustomAsset, DefaultAssetMap<String, MyCustomAsset>> store =
new HytaleAssetStore<>(
MyCustomAsset.class,
"MyCustomAssets", // Directory name
".mycustomasset", // File extension
MyCustomAsset.CODEC,
DefaultAssetMap::new
);
getAssetRegistry().register(store);
}
Troubleshooting
Textures Not Loading
- Check file path matches reference
- Verify PNG format and dimensions
- Ensure pack.json is valid
- Check console for loading errors
Models Not Displaying
- Verify model JSON syntax
- Check texture references exist
- Ensure bone hierarchy is valid
- Test with simpler model first
Sounds Not Playing
- Check OGG/WAV format compatibility
- Verify sound event references
- Check volume and category settings
- Ensure sound files exist at path
Asset Inheritance Not Working
- Verify Parent path is correct
- Check parent asset loads first
- Ensure pack dependencies are set
See references/asset-formats.md for detailed format specs.
See references/texture-specs.md for texture requirements.
More from mnkyarts/hytale-skills
hytale-plugin-basics
Create and structure Hytale server plugins with proper lifecycle, manifest, dependencies, and registries. Use when asked to "create a Hytale plugin", "make a Hytale mod", "start a new Hytale plugin", "setup plugin structure", or "write plugin boilerplate".
21hytale-networking
Handle Hytale network packets, create custom packets, and implement client-server communication. Use when asked to "create custom packets", "handle network messages", "send data to client", "receive client data", or "implement networking".
20hytale-ui-windows
Create custom UI windows, containers, and interactive interfaces for Hytale plugins. Use when asked to "create inventory UI", "make custom window", "add container interface", "build crafting UI", "custom GUI", "create .ui file", or "design UI layout".
19hytale-crafting-recipes
Create custom crafting recipes for Hytale plugins including shaped, shapeless, processing, and blueprint recipes. Use when asked to "add crafting recipe", "create recipe", "make craftable item", "add smelting recipe", or "custom crafting".
18hytale-custom-entities
Create custom entities and NPCs for Hytale with AI behaviors, components, spawning, and animations. Use when asked to "create a custom entity", "add an NPC", "make a mob", "design AI behavior", or "configure entity spawning".
18hytale-commands
Create custom commands for Hytale server plugins with arguments, permissions, and execution handling. Use when asked to "create a command", "add slash command", "make admin command", "register commands", or "command with arguments".
15