Specific (Head-Like) Rotation

experimental

expert

This tutorial guides you through making a block with proper head-like rotation (e.g. creeper heads and signs), providing examples of a "shell" block with this rotation type.

BEGINNERS BEWARE!

This tutorial assumes good knowledge of experimental blocks, advanced Molang and creating models in applications such as Blockbench.

Custom shell blocks oriented randomly

Features:

  • Can be placed on the top of blocks, with 16 possible rotations
  • Can be placed on the side faces of blocks (north, east, south & west)
  • Rotation behaves the same as vanilla mob heads - without the performance hit from block entities!

Experiments:

  • Holiday Creator Features
  • Upcoming Creator Features

Block Model

To allow for more specific rotation, your block's model will need a few extra bones.

There are 4 bones required for specific ground rotation, each with different Y axis rotations:

  • up_0 (Y rotation = 0)
  • up_22_5 (Y rotation = 22.5)
  • up_45 (Y rotation = 45)
  • up_67_5 (Y rotation = 67.5)

These rotations are in a clockwise direction.

The bones will likely be duplicates of each other, excluding rotation change.

TIP

Keep your bones' pivots set to [0, 0, 0] so that their rotation is around the middle of the block.

In addition, a side bone will be necessary for placement on side faces.

The following model for a "shell" block can be used as a reference:

Shell Example Model
RP/models/blocks/shell.geo.jsonCopy
json
{
  "format_version": "1.12.0",
  "minecraft:geometry": [
    {
      "description": {
        "identifier": "geometry.shell",
        "texture_width": 16,
        "texture_height": 16,
        "visible_bounds_width": 3,
        "visible_bounds_height": 2.5,
        "visible_bounds_offset": [0, 0.75, 0]
      },
      "bones": [
        {
          "name": "shell",
          "pivot": [0, 0, 0]
        },
        {
          "name": "up_0",
          "parent": "shell",
          "pivot": [0, 0, 0],
          "cubes": [
            {
              "origin": [-3, 0, -3],
              "size": [6, 3, 6],
              "uv": {
                "north": { "uv": [0, 6], "uv_size": [6, 3] },
                "east": { "uv": [0, 6], "uv_size": [6, 3] },
                "south": { "uv": [0, 6], "uv_size": [6, 3] },
                "west": { "uv": [0, 6], "uv_size": [6, 3] },
                "up": { "uv": [6, 6], "uv_size": [-6, -6] },
                "down": { "uv": [6, 6], "uv_size": [-6, -6] }
              }
            }
          ]
        },
        {
          "name": "up_22_5",
          "parent": "shell",
          "pivot": [0, 0, 0],
          "rotation": [0, 22.5, 0],
          "cubes": [
            {
              "origin": [-3, 0, -3],
              "size": [6, 3, 6],
              "uv": {
                "north": { "uv": [0, 6], "uv_size": [6, 3] },
                "east": { "uv": [0, 6], "uv_size": [6, 3] },
                "south": { "uv": [0, 6], "uv_size": [6, 3] },
                "west": { "uv": [0, 6], "uv_size": [6, 3] },
                "up": { "uv": [6, 6], "uv_size": [-6, -6] },
                "down": { "uv": [6, 6], "uv_size": [-6, -6] }
              }
            }
          ]
        },
        {
          "name": "up_45",
          "parent": "shell",
          "pivot": [0, 0, 0],
          "rotation": [0, 45, 0],
          "cubes": [
            {
              "origin": [-3, 0, -3],
              "size": [6, 3, 6],
              "uv": {
                "north": { "uv": [0, 6], "uv_size": [6, 3] },
                "east": { "uv": [0, 6], "uv_size": [6, 3] },
                "south": { "uv": [0, 6], "uv_size": [6, 3] },
                "west": { "uv": [0, 6], "uv_size": [6, 3] },
                "up": { "uv": [6, 6], "uv_size": [-6, -6] },
                "down": { "uv": [6, 6], "uv_size": [-6, -6] }
              }
            }
          ]
        },
        {
          "name": "up_67_5",
          "parent": "shell",
          "pivot": [0, 0, 0],
          "rotation": [0, 67.5, 0],
          "cubes": [
            {
              "origin": [-3, 0, -3],
              "size": [6, 3, 6],
              "uv": {
                "north": { "uv": [0, 6], "uv_size": [6, 3] },
                "east": { "uv": [0, 6], "uv_size": [6, 3] },
                "south": { "uv": [0, 6], "uv_size": [6, 3] },
                "west": { "uv": [0, 6], "uv_size": [6, 3] },
                "up": { "uv": [6, 6], "uv_size": [-6, -6] },
                "down": { "uv": [6, 6], "uv_size": [-6, -6] }
              }
            }
          ]
        },
        {
          "name": "side",
          "parent": "shell",
          "pivot": [0, 5, 8],
          "rotation": [90, 0, 0],
          "cubes": [
            {
              "origin": [-3, 5, 8],
              "size": [6, 3, 6],
              "uv": {
                "north": { "uv": [0, 6], "uv_size": [6, 3] },
                "east": { "uv": [0, 6], "uv_size": [6, 3] },
                "south": { "uv": [0, 6], "uv_size": [6, 3] },
                "west": { "uv": [0, 6], "uv_size": [6, 3] },
                "up": { "uv": [6, 6], "uv_size": [-6, -6] },
                "down": { "uv": [6, 6], "uv_size": [-6, -6] }
              }
            }
          ]
        }
      ]
    }
  ]
}
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

Initial Block JSON

Below is the base "shell" block we will be adding advanced rotation to.

BP/blocks/shell.jsonCopy
json
{
	"format_version": "1.19.60",
	"minecraft:block": {
		"description": {
			"identifier": "wiki:shell",
			"menu_category": {
				"category": "nature"
			}
		},
		"components": {
			// Default collision/selection boxes
			"minecraft:collision_box": {
				"origin": [-3, 0, -3],
				"size": [6, 3, 6]
			},
			"minecraft:selection_box": {
				"origin": [-3, 0, -3],
				"size": [6, 3, 6]
			},
			// Model made in previous step
			"minecraft:geometry": "geometry.shell",
			"minecraft:material_instances": {
				"*": {
					// Friendly name defined in `RP/textures/terrain_texture.json`
					"texture": "shell"
				}
			},
			"minecraft:placement_filter": {
				"conditions": [
                    {
                        // Prevent block from being placed on `down` face
                        "allowed_faces": ["up", "side"]
                    }
                ]
			}
		}
	}
}
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

Block Properties

For head-like rotation, you need to add 2 properties to your block's description:

json
"properties": {
	// Face block is placed on
	"wiki:face": [0, 1, 2, 3, 4],
	// Specific rotation of block when placed on `up` face
	"wiki:rotation": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
}
1
2
3
4
5
6

Rotation Molang Expression

Rather than manually typing bounds for each wiki:rotation value, you can use some division to return the values desired!

c
// If targeted block face is `up`, continue, otherwise 0 is returned
q.block_face == 1 ? {
	// Transform player's head Y rotation to a positive
	t.positive_head_rot = q.head_y_rotation(0) + 360 * (q.head_y_rotation(0) != Math.abs(q.head_y_rotation(0)));
	// How many 16ths of 360 is the head rotation? - rounded
	t.block_rotation = Math.round(t.positive_head_rot / 22.5);

	// 0 and 16 represent duplicate rotations (0 degrees and 360 degrees), so 0 is returned if the value of `t.block_rotation` is 16
	return t.block_rotation != 16 ? t.block_rotation;
};
1
2
3
4
5
6
7
8
9
10

On a single line so that you can put it into JSON:

c
q.block_face == 1 ? { t.positive_head_rot = q.head_y_rotation(0) + 360 * (q.head_y_rotation(0) != Math.abs(q.head_y_rotation(0))); t.block_rotation = Math.round(t.positive_head_rot / 22.5); return t.block_rotation != 16 ? t.block_rotation; };
1

Placement Event

Time to use this Molang to set the block properties you have added!

We will update our block properties when the player is placing the block. This means that in our event, we have access to q.block_face and q.head_y_rotation.

Create the following component and event in your block:

json
"components": {
	...
	"minecraft:on_player_placing": {
		"event": "wiki:set_placement"
	}
},
"events": {
	"wiki:set_placement": {
		"set_block_property": {
			// Set the face placed on
			"wiki:face": "q.block_face - 1",
			// Now use the long Molang expression from before to set the rotation property
			"wiki:rotation": "q.block_face == 1 ? { t.positive_head_rot = q.head_y_rotation(0) + 360 * (q.head_y_rotation(0) != Math.abs(q.head_y_rotation(0))); t.block_rotation = Math.round(t.positive_head_rot / 22.5); return t.block_rotation != 16 ? t.block_rotation; };"
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Part Visibility

Not all bones in your model should be visible - that would look slightly unusual - so make use of the Part Visibility component to ensure that only the required bones (or parts) are rendered:

json
"minecraft:part_visibility": {
	"conditions": {
		"up_0": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 0 || q.block_property('wiki:rotation') == 4 || q.block_property('wiki:rotation') == 8 || q.block_property('wiki:rotation') == 12)",
		"up_22_5": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 1 || q.block_property('wiki:rotation') == 5 || q.block_property('wiki:rotation') == 9 || q.block_property('wiki:rotation') == 13)",
		"up_45": "!q.block_property('wiki:face')  && (q.block_property('wiki:rotation') == 2 || q.block_property('wiki:rotation') == 6 || q.block_property('wiki:rotation') == 10 || q.block_property('wiki:rotation') == 14)",
		"up_67_5": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 3 || q.block_property('wiki:rotation') == 7 || q.block_property('wiki:rotation') == 11 || q.block_property('wiki:rotation') == 15)",
		"side": "q.block_property('wiki:face')"
	}
}
1
2
3
4
5
6
7
8
9

Block Permutations

In addition to the variants of your model in bones, we will further rotate the block through the minecraft:rotation component in permutations. The reason behind having multiple bones is that minecraft:rotation only supports multiples of 90 degrees, while head rotation requires 22.5 degree steps.

Insert the following permutations into your block JSON (in the presented order):

json
"permutations": [
	{
		"condition": "(!q.block_property('wiki:face') && q.block_property('wiki:rotation') >= 4) || q.block_property('wiki:face') == 4",
		"components": {
			"minecraft:rotation": [0, -90, 0]
		}
	},
	{
		"condition": "(!q.block_property('wiki:face') && q.block_property('wiki:rotation') >= 8) || q.block_property('wiki:face') == 2",
		"components": {
			"minecraft:rotation": [0, 180, 0]
		}
	},
	{
		"condition": "(!q.block_property('wiki:face') && q.block_property('wiki:rotation') >= 12) || q.block_property('wiki:face') == 3",
		"components": {
			"minecraft:rotation": [0, 90, 0]
		}
	}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

If you would like your block to have a different collision/selection box when placed on the side of a block, as with my "shell" block, add something similar to the following permutation:

json
"permutations": [
	...
	{
		"condition": "q.block_property('wiki:face')",
		"components": {
			"minecraft:collision_box": {
				"origin": [-3, 5, 5],
				"size": [6, 6, 3]
			},
			"minecraft:selection_box": {
				"origin": [-3, 5, 5],
				"size": [6, 6, 3]
			}
		}
	}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Final Block JSON

Our block JSON file after the above steps should look similar to the code below:

Shell Example Block JSON
BP/blocks/shell.jsonCopy
json
{
  "format_version": "1.19.60",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:shell",
      "menu_category": {
        "category": "nature"
      },
      "properties": {
        "wiki:face": [0, 1, 2, 3, 4],
        "wiki:rotation": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
      }
    },
    "components": {
      "minecraft:collision_box": {
        "origin": [-3, 0, -3],
        "size": [6, 3, 6]
      },
      "minecraft:selection_box": {
        "origin": [-3, 0, -3],
        "size": [6, 3, 6]
      },
      "minecraft:geometry": "geometry.shell",
      "minecraft:material_instances": {
        "*": {
          "texture": "shell"
        }
      },
      "minecraft:placement_filter": {
        "conditions": [
          {
            "allowed_faces": ["up", "side"]
          }
        ]
      },
      "minecraft:on_player_placing": {
        "event": "wiki:set_placement"
      },
      "minecraft:part_visibility": {
        "conditions": {
          "up_0": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 0 || q.block_property('wiki:rotation') == 4 || q.block_property('wiki:rotation') == 8 || q.block_property('wiki:rotation') == 12)",
          "up_22_5": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 1 || q.block_property('wiki:rotation') == 5 || q.block_property('wiki:rotation') == 9 || q.block_property('wiki:rotation') == 13)",
          "up_45": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 2 || q.block_property('wiki:rotation') == 6 || q.block_property('wiki:rotation') == 10 || q.block_property('wiki:rotation') == 14)",
          "up_67_5": "!q.block_property('wiki:face') && (q.block_property('wiki:rotation') == 3 || q.block_property('wiki:rotation') == 7 || q.block_property('wiki:rotation') == 11 || q.block_property('wiki:rotation') == 15)",
          "side": "q.block_property('wiki:face')"
        }
      }
    },
    "events": {
      "wiki:set_placement": {
        "set_block_property": {
          "wiki:face": "q.block_face - 1",
          "wiki:rotation": "q.block_face == 1 ? { t.positive_head_rot = q.head_y_rotation(0) + 360 * (q.head_y_rotation(0) != Math.abs(q.head_y_rotation(0))); t.block_rotation = Math.round(t.positive_head_rot / 22.5); return t.block_rotation != 16 ? t.block_rotation; };"
        }
      }
    },
    "permutations": [
      {
        "condition": "(!q.block_property('wiki:face') && q.block_property('wiki:rotation') >= 4) || q.block_property('wiki:face') == 4",
        "components": {
          "minecraft:rotation": [0, -90, 0]
        }
      },
      {
        "condition": "(!q.block_property('wiki:face') && q.block_property('wiki:rotation') >= 8) || q.block_property('wiki:face') == 2",
        "components": {
          "minecraft:rotation": [0, 180, 0]
        }
      },
      {
        "condition": "(!q.block_property('wiki:face') && q.block_property('wiki:rotation') >= 12) || q.block_property('wiki:face') == 3",
        "components": {
          "minecraft:rotation": [0, 90, 0]
        }
      },
      {
        "condition": "q.block_property('wiki:face')",
        "components": {
          "minecraft:collision_box": {
            "origin": [-3, 5, 5],
            "size": [6, 6, 3]
          },
          "minecraft:selection_box": {
            "origin": [-3, 5, 5],
            "size": [6, 6, 3]
          }
        }
      }
    ]
  }
}
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

Result

What you have created:


Custom shell blocks in a square formation, each pointing towards the middle

Download Example Pack

Template pack made according to this tutorial, adding a "shell" block into the Nature tab.

Download MCADDON
Download ZIP

Contributors

UspelOrg