Create a custom Entity

Last time you created a custom item for this custom entity to drop. This time you will create the Ghost itself, as promised.

Behavior code

Just like with items, the main files of a custom entity are its RP and BP files. We'll start with the latter in the BP/entities/ folder: (the .e suffix after the entity's name specifies that this JSON is an entity file. Recommended in the Style Guide).

BP/entities/ghost.e.jsonCopy
{
	"format_version": "1.16.0",
	"minecraft:entity": {
		"description": {
			"identifier": "wiki:ghost",
			"is_summonable": true,
			"is_spawnable": true,
			"is_experimental": false
		},
		"components": {
			"minecraft:type_family": {
				"family": ["ghost", "monster"]
			},
			"minecraft:jump.static": {},
			"minecraft:can_climb": {},
			"minecraft:physics": {},
			"minecraft:movement.basic": {},
			"minecraft:loot": {
				"table": "loot_tables/entities/ghost.json"
			},
			"minecraft:health": {
				"value": 20,
				"max": 20
			},
			"minecraft:collision_box": {
				"width": 1,
				"height": 2
			},
			"minecraft:movement": {
				"value": 0.2
			},
			"minecraft:behavior.delayed_attack": {
				"priority": 0,
				"attack_once": false,
				"track_target": true,
				"require_complete_path": false,
				"random_stop_interval": 0,
				"reach_multiplier": 1.5,
				"speed_multiplier": 1,
				"attack_duration": 0.75,
				"hit_delay_pct": 0.5
			},
			"minecraft:navigation.walk": {
				"can_walk": true,
				"avoid_sun": true,
				"can_pass_doors": true,
				"can_open_doors": true
			},
			"minecraft:attack": {
				"damage": 3
			},
			"minecraft:behavior.random_look_around": {
				"priority": 7
			},
			"minecraft:behavior.hurt_by_target": {
				"priority": 1
			},
			"minecraft:behavior.nearest_attackable_target": {
				"priority": 2,
				"within_radius": 25,
				"reselect_targets": true,
				"entity_types": [
					{
						"filters": {
							"any_of": [
								{
									"test": "is_family",
									"subject": "other",
									"value": "player"
								}
							]
						},
						"max_dist": 35
					}
				]
			},
			"minecraft:behavior.random_stroll": {
				"priority": 6,
				"speed_multiplier": 1
			},
			"minecraft:behavior.look_at_player": {
				"priority": 7,
				"look_distance": 6,
				"probability": 0.02
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  • You can already recognize parts of the file syntax from the custom item creation, such as "format_version" and "description"/"identifier". We use the same namespace in all the pack files. In our example case, the namespace is wiki. The entity's id is ghost.

  • The other keys in "description" all take Booleans (true or false) as their values. These are: "is_summonable" (whether you can spawn the entity with a /summon command), "is_spawnable" (whether you can spawn one with a spawn egg or naturally), and "is_experimental" (whether you need to turn on [EX] (Experimental mode) in a world for this entity to exist). Set the options to true, true and false respectively. We do not need [EX].

  • The next section is "components". Again, just like with items, components define the main traits of an entity. Let's go over every single one in this example:

    • "minecraft:type_family" lists the family tags of your custom entity. These are used to interact with other entities. For example, Wolves will attack all entities from the sheep and cow families, while Villagers will flee from entities from the monster family.
    • "minecraft:jump.static" (the entity's jumps are of the static type), "minecraft:can_climb" (the entity can climb blocks), "minecraft:physics" (the entity is affected by gravity), "minecraft:movement.basic" (the entity's moves using basic patterns) all take nothing but an empty object {} as their values. This is because simply having an attribute listed will ALWAYS apply its traits to the entity. There is a way to temporarily enable and disable attributes using component_groups and events, but that's for later.
    • "minecraft_loot" references a file in the pack, "loot_tables/entities/ghost.json" under its "table" key. That file defines what items the entity will drop upon death, and we will create it right after finishing the Ghost.
    • "minecraft:health" gives the Ghost 20 health points, which are equivalent to 10 hearts in-game.
    • "minecraft:collision_box" defines the size of the mob's hitbox in blocks.
    • "minecraft:movement" is, of course, the movement speed.
    • "minecraft:behavior.delayed_attack" is a behavior attribute, which allows the entity to execute some behavior patterns like, in this case, attacking. Its most important options are:
      • "priority" is often seen in components. If a mob can execute two actions simultaneously, the lower behavior integer will get picked. 0 means the action is always preferred over everything else.
      • You can look up the rest of the options on the official Documentation, bedrock.dev/r/Entities
    • "minecraft:navigation.walk" allows the mob to use its "movement.basic" goal and "movement" to navigate by walking. The options inside define if the mob should avoid sun, pass doors and if it can walk and open doors itself.
    • "minecraft:attack" / "damage" defines the amount of damage the mob deals upon an attack
    • "minecraft:behavior.random_look_around" allows the mob to sometimes look around itself;
    • "minecraft:behavior.hurt_by_target" makes the entity immediately choose the entity that damages/hurts them as their target. For example, a Zombie Piglin will act this way.
    • "minecraft:behavior.nearest_attackable_target" allows the mob to select targets itself (within the radius of 25) as a hostile mob. The "filters" in "entity_type" will make sure the target belongs to the player family in our case, but you could also make it attack villagers, for example, by changing everything inside " filters": {} to
    Copy
    "any_of": [
                {
                    "test": "is_family",
                    "subject": "other",
                    "value": "player"
                },
                {
                    "test": "is_family",
                    "subject": "other",
                    "value": "villager"
                }
            ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    • "minecraft:behavior.random_stroll" makes the entity stroll around sometmes;
    • And, finally, "minecraft:behavior.look_at_player" makes the entity follow the player with its head.

Resource code

Again, just like with the item, a custom entity needs a resource file listing its model, texture, and animation names.

RP/entity/ghost.e.jsonCopy
{
	"format_version": "1.10.0",
	"minecraft:client_entity": {
		"description": {
			"identifier": "wiki:ghost",
			"materials": {
				"default": "entity_alphatest"
			},
			"textures": {
				"default": "textures/entity/ghost"
			},
			"geometry": {
				"default": "geometry.ghost"
			},
			"scripts": {
				"animate": ["walk_controller", "attack_controller"]
			},
			"animations": {
				"walk_controller": "controller.animation.ghost.walk",
				"attack_controller": "controller.animation.ghost.attack",
				"attack": "animation.ghost.attack",
				"idle": "animation.ghost.idle",
				"move": "animation.ghost.move"
			},
			"spawn_egg": {
				"overlay_color": "#bdd1d1",
				"base_color": "#9fb3b3"
			},
			"render_controllers": ["controller.render.ghost"]
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Let's go over every single object in "description" , as usual:

  • "identifier" is, of course, the entity's namespace:id. This tells the game that this file belongs to the same entity as the one defined in BP/entities/ghost.e.json.
  • "materials" tell the game how to render our entity. Depending on which material you choose, the translucent pixels on the texture might glow, be transparent, or have no effect. We assigned entity_alphatest the shortname default (precisely like with item textures) to allow partly transparent textures, making sense for a ghost. Custom materials are also possible, but they're more complicated. (You can learn more about materials in general here).
  • "textures"/"default" is the path (relative to the RP folder) to the entity's texture. Like in item_textures.json, the .png extension can be ignored. Again, the texture path is assigned to the shortname "default". In some cases, like the Villager mob, you'd want to have multiple textures for the entity to switch between.
  • "geometry" takes the model's identifier as its value; Note it for yourself, since we'll create the model itself in a moment. Again, "default" is the shortname for that.
  • "scripts"/"animate" and "animations" control when animations are to be played with the help of animation controllers and list them under their shortnames respectively. Again, we'll come back to this once we have the animations ready.
  • "spawn_egg" automatically creates an item that will spawn the custom entity on use. It can look in two different ways: one of them can be seen here: both the "overlay_color" and "base_color" are defined with hex codes in the form of text. The other way would be changing the whole thing to
Copy
 "spawn_egg": {
    "texture": "wiki.ectoplasm"
}
1
2
3

You can probably remember that "wiki.ectoplasm" is the shortname of our custom item's texture. If you use this code snippet, the egg will use the same texture as the item instead of a procedurally generated traditional spawn egg.

  • And, finally, "render_controllers" lists one or more identifiers of render controllers, which control materials.

Our next step is creating the mentioned render controller with the id controller.render.ghost. Since our Ghost's texture, model and materials always remain the same; the following code is quite simple. However, more advanced render controllers typically enable dynamic switching between these. You can learn more about render controlles here.

RP/render_controllers/entity/ghost.rc.jsonCopy
{
	"format_version": "1.10.0",
	"render_controllers": {
		"controller.render.ghost": {
			"geometry": "geometry.default",
			"materials": [
				{
					"*": "material.default"
				}
			],
			"textures": ["texture.default"]
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

The id of the only render controllers in this file is "controller.render.ghost", which was referenced in the RP entity folder of the ghost (RP/entity/ghost.e.json/"render_controllers"). The code inside tells the game the geometry.

Visuals

Now it's time to create the entity's visuals. An example model, texture, and animation are all already provided with this Guide, but you can learn how to create your own using Blockbench here.

Texture

Like items, the entity textures are simply .png files in RP/textures/entity/. However, unlike these, entity textures don't need a file equivalent to item_textures.json. Instead, their shortnames are defined in the entity's RP file (RP/entity/ghost.e.json in our case). Usually, the shortname for an entity's texture is set to default, just like in our example.

RP/textures/entity/ghost.pngGhost_texture

Model

'Model' means 'shape' or 'geometry'. Entity model JSON files are located in RP/models/entity/ and, according to the Style guide, use the suffix .geo. Before you take a look at a model's code, which holds data about the size, rotation, and position of every single cube and bone, remember that there's no need to learn its syntax by hard: as mentioned above, model and animation files are all automatically generated by a dedicated 3D editor called Blockbench.

RP/models/entity/ghost.geo.jsonCopy
{
	"format_version": "1.12.0",
	"minecraft:geometry": [
		{
			"description": {
				"identifier": "geometry.ghost",
				"texture_width": 64,
				"texture_height": 64,
				"visible_bounds_width": 3,
				"visible_bounds_height": 3.5,
				"visible_bounds_offset": [0, 1.25, 0]
			},
			"bones": [
				{
					"name": "root",
					"pivot": [0, 3, 0]
				},
				{
					"name": "body",
					"parent": "root",
					"pivot": [0, 4.625, 0],
					"cubes": [
						{
							"origin": [-4, 3, -4],
							"size": [8, 13, 8],
							"uv": [0, 20]
						}
					]
				},
				{
					"name": "leftArm",
					"parent": "body",
					"pivot": [4.6, 15.5, 0.5],
					"cubes": [
						{
							"origin": [4.1, 7, -1],
							"size": [3, 9, 3],
							"uv": [32, 32]
						}
					]
				},
				{
					"name": "rightArm",
					"parent": "body",
					"pivot": [-4.5, 15.5, 0.5],
					"cubes": [
						{
							"origin": [-7.1, 7, -1],
							"size": [3, 9, 3],
							"uv": [32, 20]
						}
					]
				},
				{
					"name": "head",
					"parent": "body",
					"pivot": [0, 16, 0],
					"cubes": [
						{
							"origin": [-5, 16, -5],
							"size": [10, 10, 10],
							"uv": [0, 0]
						}
					]
				}
			]
		}
	]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

The only important thing, pretty much, is the value of "identifier", which is "geometry.ghost". As you might remember, this same identifier is referenced in the Ghost's RP entity file, under "geometry".

Animations

Most of what was said for models is also true for animations. Here's the code you ought to copy to RP/animations/ghost.a.json. The suffix for resource animation files is .a.

RP/animations/ghost.a.jsonCopy
{
	"format_version": "1.8.0",
	"animations": {
		"animation.ghost.idle": {
			"loop": true,
			"animation_length": 3,
			"bones": {
				"body": {
					"rotation": {
						"0.0": [10, 0, 0],
						"3.0": [10, 0, 0]
					},
					"position": {
						"0.0": [0, 0, 0],
						"1.5": [0, 1, 0],
						"3.0": [0, 0, 0]
					}
				},
				"leftArm": {
					"rotation": {
						"0.0": [-10, 0, 0],
						"1.5": [-5, 0, 0],
						"3.0": [-10, 0, 0]
					}
				},
				"rightArm": {
					"rotation": {
						"0.0": [-10, 0, 0],
						"1.5": [-5, 0, 0],
						"3.0": [-10, 0, 0]
					}
				},
				"head": {
					"rotation": {
						"0.0": [-7.5, 0, 0],
						"1.5": [-2.5, 0, 0],
						"3.0": [-7.5, 0, 0]
					}
				}
			}
		},
		"animation.ghost.attack": {
			"animation_length": 0.75,
			"bones": {
				"body": {
					"rotation": {
						"0.0": [10, 0, 0],
						"0.2917": [10, 15, 0],
						"0.5": [22.5, -12.5, 0],
						"0.75": [10, 0, 0]
					},
					"position": {
						"0.0": [0, 0, 0],
						"0.2917": [0, 0, 3],
						"0.5": [0, 0, -3],
						"0.75": [0, 0, 0]
					}
				},
				"leftArm": {
					"rotation": {
						"0.0": [-10, 0, 0],
						"0.75": [-10, 0, 0]
					}
				},
				"rightArm": {
					"rotation": {
						"0.0": [-10, 0, 0],
						"0.2083": [-10, 0, 0],
						"0.2917": [-10, 62.5, 117.5],
						"0.5": [-80, -17.5, 22.5],
						"0.75": [-10, 0, 0]
					}
				},
				"head": {
					"rotation": {
						"0.0": [-7.5, 0, 0],
						"0.75": [-7.5, 0, 0]
					}
				}
			}
		},
		"animation.ghost.move": {
			"loop": true,
			"animation_length": 1,
			"bones": {
				"body": {
					"rotation": {
						"0.0": [15, 0, 0],
						"0.25": [15, -2.5, 0],
						"0.5": [15, 0, 0],
						"0.75": [15, 2.5, 0],
						"1.0": [15, 0, 0]
					},
					"position": [0, 0, 0]
				},
				"leftArm": {
					"rotation": {
						"0.0": [15, 0, 0],
						"0.5": [20, 0, 0],
						"1.0": [15, 0, 0]
					}
				},
				"rightArm": {
					"rotation": {
						"0.0": [15, 0, 0],
						"0.5": [20, 0, 0],
						"1.0": [15, 0, 0]
					}
				},
				"head": {
					"rotation": {
						"0.0": [-12.5, 0, 0],
						"0.5": [-15, 0, 0],
						"1.0": [-12.5, 0, 0]
					}
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

Unlike the model's file, this one contains three animations for the Ghost, which are "animation. Ghost.idle" (which is an animation playing from time to time), "animation.ghost.attack" and "animation.ghost.move" (quite self-explanatory). Their shortnames, as defined in the Ghost's RP entity file, are "idle", "attack" and "move" respectively. Of course, feel free to open the files and check how the visuals look using Blockbench.

Animation Controller

Approaching the end, we'll create our Animation Controller file, which will control how the animations are played, as expected.

RP/animation_controllers/ghost.ac.jsonCopy
{
	"format_version": "1.12.0",
	"animation_controllers": {
		"controller.animation.ghost.attack": {
			"states": {
				"default": {
					"transitions": [
						{
							"attacking": "query.is_delayed_attacking"
						}
					]
				},
				"attacking": {
					"blend_transition": 0.2,
					"animations": ["attack"],
					"transitions": [
						{
							"default": "!query.is_delayed_attacking"
						}
					]
				}
			}
		},
		"controller.animation.ghost.walk": {
			"initial_state": "standing",
			"states": {
				"standing": {
					"blend_transition": 0.2,
					"animations": ["idle"],
					"transitions": [
						{
							"moving": "query.modified_move_speed > 0.1"
						}
					]
				},
				"moving": {
					"blend_transition": 0.2,
					"animations": ["move"],
					"transitions": [
						{
							"standing": "query.modified_move_speed < 0.1"
						}
					]
				}
			}
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

Two animation controllers are listed in this file: "controller.animation.ghost.walk" and "controller.animation.ghost.attack".

TODO: Details of each NEED to be explained better

Let's take a look at the Ghost's RP file: just like the animations, the animation controllers get assigned to their shortnames (walk_controller and attack_controller respectively) under "animations". Now, since the controllers control the animations, they have to constantly run, which is why we put their shortnames in the array of "scripts"/"animate". (For example, if you put the shortname "move" there, the entity would constantly play the moving animation, even when staying in place. The controller only launches the relevant animation when the entity is doing a certain action, for example, is_walking.)

from RP/entity/ghost.e.json

Copy
"scripts": {
    "animate": [
        "walk_controller",
        "attack_controller"
    ]
},
"animations": {
    "walk_controller": "controller.animation.ghost.walk",
    "attack_controller": "controller.animation.ghost.attack",
    "attack": "animation.ghost.attack",
    "idle": "animation.ghost.idle",
    "move": "animation.ghost.move"
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Entity name

And finally, we have to define the entity's and its spawn egg's in-game names in en_US.lang by adding these lines:

RP/texts/en_US.langCopy
entity.wiki:ghost.name=Ghost
item.spawn_egg.entity.wiki:ghost.name=Ghost
1
2

Done! Your entity should now show up in Minecraft, complete with all behaviors and visuals, including animations!

Your progress so far:

What you've done:

  • [x] Setup your pack;
  • [x] Create a custom item;
  • [x] Create a custom entity;

What are you to do next:

  • [ ] Create the entity's loot, spawn rules, and a custom recipe;

Contributors

SirLichDrGuileDish1MedicalJewel105cda94581SmokeyStack