Modding:Content Tutorial Entity Creation Part 1

From Vintage Story Wiki
Other languages:
  • English

This page was last verified for Vintage Story version 1.21.0.


Entity Creation Part 1 - Creating & Registering

Introduction

Welcome to the first advanced tutorial. This is the first of a two-part tutorial on creating, registering, and implementing a creature/entity into the game.

Concept/Implementation Split

This tutorial is quite large, so each section has been split into two further sections to help understanding. The 'concept' section will give a list of what needs to be achieved and why, and the 'Implementation' section will give the steps to actually achieve that.

Objective

In this tutorial, you will be creating and registering a creature, specifically a type of hare known as a jackrabbit. For the purpose of the tutorial, the jackrabbit will be extremely similar to the existing in-game hare.

The model and textures have already been created and provided in the setup files. If you want to see how to create an entity model, take a look at the VS Modeler tutorial pages.

Assets

Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here.

For this tutorial, you will want to start with the 'entity-tutorial-setup.zip' file.

Prerequisites

It is recommended to have completed the basic and intermediate tutorial sets. This tutorial is significantly more complex than previous tutorials, so a solid knowledge of JSON may be required.

Navigating Assets

This tutorial contains a few pre-existing asset files.

In the setup folder, take a look at the following files:

  • entities/jackrabbit.json. An empty file that will be used to create the entity.
  • itemtypes/creature.json. An empty file that will be used to create the item to spawn the entity.
  • lang/en.json. A lang file with pre-completed entries for the entity.
  • shapes/entity/jackrabbit.json. A shape file with animations and textures, based off the vanilla hare shape.
  • textures/entity/jackrabbit-x. A set of four textures for the jackrabbit. These will be randomly selected by the entity file when a creature is spawned.

Registering the Entity

So that you have something you can test, let’s start with actually registering the entity.

Concept

Entities have a few base properties, however most of their functionality comes under “client” and “server” properties. You can find the JSON documentation for these classes here: EntityType, ClientEntityConfig, ServerEntityConfig.

  • Entities require a unique code.
  • Entities require an entity class. Most living entities will use ‘EntityAgent’.
  • Entities should have an associated itemtype using the ‘ItemCreature’ class to spawn them in. This is essentially the ‘held’ version of the entity.
  • Entities require a list of client behaviors and a list of server behaviors. These can be empty, but they need to exist for the entity to register.

Implementation

Start by accessing the jackrabbit entity file. The following properties need to be added:

"code": "jackrabbit",
"class": "EntityAgent",

As mentioned earlier, the entity also needs to define a client and server behavior sets. These can be empty, but they have to exist:

"client": {
	"behaviors": []
},
"server": {
	"behaviors": []
}

This is all that is actually needed to register an entity in the game. Other properties will affect how the entity functions and looks. The client and server properties will have a lot of properties within them - So keep a close eye on where you put each json property.

Now move on to the creature itemtype file. The contents of this file are based off of the itemtypes/creature.json in the base survival assets.

The itemtype is exceptionally simple - It requires the ‘ItemCreature’ class, and a variant called ‘type’ to specify what entity should be spawned from the item. The rest of the file is mainly just graphical.

Note that the ‘type’ variant group will automatically get prefixed with your asset domain to access the entity.

creatures.json
{
	"code": "creature",
	"class": "ItemCreature",
	"maxstacksize": 64,
	"variantgroups": [
		{
			"code": "type",
			"states": [
				"jackrabbit"
			]
		}
	],
	"shapeByType": {
		"*-jackrabbit": {
			"base": "entity/jackrabbit"
		}
	},
	"texturesByType": {
		"*-jackrabbit": { "skin": { "base": "entity/jackrabbit-1" } }
	},
	"creativeinventory": {
		"general": [ "*" ],
		"items": [ "*" ],
		"creatures": [ "*" ],
		"examplemod": [ "*" ],
	},
	"materialDensity": 600,
	"guiTransformByType": {
		"*-jackrabbit": {
			"rotate": true,
			"translation": {
				"x": 2,
				"y": 0,
				"z": 0
			},
			"rotation": {
				"x": 4,
				"y": -59,
				"z": -180
			},
			"origin": {
				"x": 0.54,
				"y": 0.28,
				"z": 0.5
			},
			"scale": 2.6
		}
	},
	"tpHandTransformByType": {
		"*-jackrabbit": {
			"translation": {
				"x": -0.8,
				"y": -0.4,
				"z": -0.5
			},
			"rotation": {
				"x": -180,
				"y": 0,
				"z": -156
			},
			"scale": 0.7
		}
	}
}

Now both of these are in place, you can launch the game. Grab the jackrabbit from the creative menu, try placing your entity and…

It crashed, didn’t it? Don’t worry, that’s to be expected.

Rendering the Entity

The game crashes due to the rendering properties not actually being defined. Unlike the previous block and item tutorials, there is no failsafe or default for entities.

Concept

Rendering is done fully on the client side, so the following changes will need to be added to the inside of the “client” property.

  • Set the “renderer” type to “shape”, as we’re using a JSON shape.
  • Specify the shape using the “shape” property.
  • Specify the textures and use the “alternates" property to define multiple random textures.

Implementation

In truth, the JSON is practically identical to the block and item types. The only difference is that this time we’re going to use the alternates property to specify multiple different textures for our entities without using variants.

As mentioned earlier, all rendering is done on the client-side. In the “client” property, add the following:

"renderer": "Shape",
"shape": { "base": "entity/jackrabbit" },
"textures": {
		"skin": {
			"base": "entity/jackrabbit-1",
			"alternates": [
				{
					"base": "entity/jackrabbit-2"
				},
				{
					"base": "entity/jackrabbit-3"
				},
				{
					"base": "entity/jackrabbit-4"
				}
			]
		}
},

Note how the alternate textures work. We specify a single texture, and then use the alternates to specify more than a single texture. The game will automatically randomly pick one of the four textures for the entity upon creation, and this will be saved with the entity’s data.  

Now, once again, have a go at spawning the entity. This time, they should spawn correctly with the right shape and a randomly picked texture.

Try attacking the entity. You’ll notice that, because they are using the EntityAgent class, the default logic for taking damage and dealing with attacks already exists. However, the entity doesn’t actually have health and cannot die.

You’ll notice that regardless of your actions, the entity will not move. Let’s move on to giving it some animations.

Adding Animations

Concept

This section will not talk about creating animations for a model. If you want that, please see the VS Model Creator tutorials. The jackrabbit, as it is based off the base-game hare, has a small set of animations. A few of these are used by the EntityAgent class, where others will be used later when we start adding AI to the entity.

  • All animations are client-side, and are therefore contained inside the “client” property.
  • Animations are added using the “animations” property array.
  • Each animation requires a unique “code” property to use in-game, and a reference to the unique “animation” code property in the model. These two properties are often the same.
  • Animations can contain other data, such as “animationSpeed”, “weight”, “blendMode”, “easeOut/InSpeed”, and more. The full list can be found at AnimationMetaData.
  • Animations can use the “triggeredBy” property to control when they are used. For example, the “idle” animation will use the “defaultAnim”: “true” property, whereas the death animation uses the “onControls”: [ “dead” ] property.
  • Some animations, such as “hurt”, are automatically referenced and used by the class, and do not need special functionality to work.

Implementation

Start by adding in the “animations” property array to the client:

"client": {
	
	"animations": [
	
	],
	
}

There’s a total of 7 animations to add to the entity, but to start of gently, just add the one:

"animations": [
		{
			"code": "hurt",
			"animation": "hurt",
			"animationSpeed": 2.2,
			"weight": 10,
			"blendMode": "AddAverage"
		},
]

As mentioned earlier, there’s a few properties here. The “code” property defines a unique code to use the animation in game, and the “animation” property is the name of the animation in the shape file. “AnimationSpeed” is a multiplier that controls the speed of the animation. “Weight”,“Blendmode”, and the later-used “EaseIn/OutSpeed” are quite specific - they control how the animation merges with other animations. These are quite specific and may require some tuning to get right. For now, the mod copies the values directly from the base-game hare.

Here is the full animation property:

"animations" Property
"animations": [
    {
    	"code": "hurt",
    	"animation": "hurt",
    	"animationSpeed": 2.2,
    	"weight": 10,
    	"blendMode": "AddAverage"
    },
    {
    	"code": "dig",
    	"animation": "dig",
    	"animationSpeed": 1
    },
    {
    	"code": "longdig",
    	"animation": "longdig",
    	"animationSpeed": 1
    },
    {
    	"code": "die",
    	"animation": "death",
    	"animationSpeed": 1.25,
    	"weight": 10,
    	"blendMode": "Average",
    	"triggeredBy": { "onControls": [ "dead" ] }
    },
    {
    	"code": "idle",
    	"animation": "idle",
    	"blendMode": "AddAverage",
    	"easeOutSpeed": 4,
    	"triggeredBy": { "defaultAnim": true }
    },
    {
    	"code": "sleep",
    	"animation": "sleep",
    	"easeInSpeed": 4,
    	"easeOutSpeed": 4,
    	"blendMode": "Average"
    },
    {
    	"code": "sit",
    	"animation": "sit",
    	"easeInSpeed": 4,
    	"easeOutSpeed": 4,
    	"blendMode": "Average"
    }
]

Notice the “triggeredBy” syntax on the death and idle animations.

After adding these, reload the world (Ctrl+F1) and the placed jackrabbit will now have an idle animation. It’s quite slight, but you’ll notice them breathing. As well as this, give them a slight slap and they’ll play their hurt animation. The rest of the animations will be used when we implement the jackrabbit AI and start adding behaviors in the next tutorial.

Hitbox, Eye Height & Sounds

Concept

All of the following are contained outside of the client and server properties, as they are shared between the two.

  • HitboxSize” is a Vec2f (2-dimensional float value) that controls the size of the collision and hitbox for the entity.
  • EyeHeight” is a float that controls a few different values. If the player were to control this entity, this is where they will see from.
  • Sounds” is a dictionary that contains a list of sound assets for an entity. The syntax is available here: Entity Sounds
  • IdleSoundChance” is a float that controls how regularly the entity will play its idle sound.

Implementation

Start by adding the hitbox size to the entity:

"hitboxSize": {
	"x": 0.75,
	"y": 0.5
},

This will automatically set both the collision and selection boxes for the entity. It is worth noting that an entity hitbox is required to have the same width in the X and Z axes. The sizes are relative to a single 1x1x1 block. Adding the eye height property is simple:

"eyeHeight": 0.6,

Moving onto the sounds…

"sounds": {
	"hurt": "game:creature/hare-hurt",
	"death": "game:creature/hare-hurt",
	"idle": "game:creature/hare-idle"
},
"idleSoundChance": 0.03

Most entities will include hurt, death, and idle sounds in the files. Note that, as we’re using the game domain, these will be found in assets/survival/sounds/creature/hare-... for their respective sounds.

The idleSoundChance property will control how often the idle sound is played.

After adding these properties to the entity, you should be able to test that the sounds will play. Hitboxes are often hard to test and may often require some fine-tuning, however the hitbox used is copied from the base-game hare. You can use the command ‘.debug wireframe entity’ to view entity hitboxes.

Conclusion

Congratulations, you’ve now registered and created an entity in the game! Of course, it’s quite useless right now without an AI, but that’s why you should now move on to the next tutorial!

Next Steps

In the next tutorial, you’ll learn how to add an AI and behaviors to the entity, as well as looking at how to naturally spawn the entity in the world.