Создание игровых аддонов и текстур — minecraft

от автора

Привет, Хабр! В этой статье я хочу затронуть тему аддонов. Многим чего-то не хватало в игре, и они скачивали аддоны с интернета. Когда вы понимаете, что скачанный аддон настолько безполезный, удаляете его. Я, допустим, хочу сделать аддон в minecraft bedrock. Если я сделаю достойный аддон, он может попасть на рынок. Заманчиво? Давайте попробуем сделать что-нибудь своё. Пригодится базовое знание json и в некоторых случаях javascript.

Первым делом, создадим папку myaddon по пути android/data/com.mojang.../files//games/com.mojang/behaivor_packs если у вас другое сохранение файлов, используйте games/com.mojang.../... Название папки, которую вы создали, никак не влияет на название аддона. За это отвечает manifest.json, который находится в нашей папке. Отредакиируем его

{     "format_version": 1,     "header": {         "description": "Описание вашего аддона"         "name": "Название вашего аддона",         "uuid": "вставьте сюда уникальный id",         "version": [ 1, 0, 5 ],         "min_engine_version": [ 1, 17, 10 ]     },     "modules": [         {             "description": "",             "type": "data",             "uuid": "вставьте сюда второй уникальный id",             "version": [1, 0, 0]         }     ] }

Если что, uuid это уникальный id, который используется для сохранения аддона. Уникальный айди можно сгенерировать, например, на сайте uuidgener*tor.net, я не хочу оставлять точную ссылку поэтому вместо «а» поставил «*». Чтобы создать иконку нашему аддону, создайте в папке аддона файл pack_icon.jpg или pack_icon.png. Теперь сохраняем наши файлы, заходим в майнкрафт и проверям — теперь во вкладке создание мира в «наборы параметров» есть пустой аддон, который ещё бесполезный.

Function — так называется функция аддона, которая может выполнять несколько команд одновременно. Например, если вы в чат введёте команду /function myfunction, вам дадут и яблоко, и дерево. Так можно создавать на function даже нормальные античиты, но для этого требуется знание команд minecraft bedrock. Чтобы сделать функции в аддона, создайте в папке аддона папку functions. В ней создадите myfunction.mcfunction, ваша функция будет называться myfunction. Отредактируйте код.

#выдать всём яблоко give @a apple  #выдать всем дерево give @a log  #создать скорборд scoreboard objectives add my score dummy

Функция может не отображаться при вводе /function ****. Теперь, если вы введёте /function myfunction, всем игрокам в мире дадут яблоко и дерево, а ещё создадится новый скорборд. Чтобы функция работала каждый тик непрерывно, не вводя её в чат или командный блок, создадим в папке function файл tick.json. Он будет отвечать за автоматическое выполнение функций каждый тик. Введём небольшой json код

{   "values": [     "myfunction"   ] }

теперь функция myfunction будет выполняться каждый тик. Если что, одна секунда это 20 тиков.

Идём всё дальше — чат. Вы, наверное, давно хотели оптимизировать чат, сделать ранги, антиспам. Тут всё в одном аддона — антиспам, ранги в чате и тд. Для начала нам придётся изменить manifest.json, чтобы указать в нём содержание javascript.

{    "format_version": 2,    "header": {      "name": "Название аддона",      "description": "Описание аддона",      "uuid": "Уникальный id",      "version": [ 1, 0, 3 ],      "min_engine_version": [ 1, 14, 0 ]    },    "modules": [      {        "description": "made by habr @DinoZavr2",        "type": "data",        "uuid": "Второй уникальный id",        "version": [ 1, 0, 0 ]      },      {        "description": "",        "language": "javascript",        "type": "script",        "uuid": "Третий уникальный id",        "version": [0, 0, 1],        "entry": "scripts/main/index.js"      }    ],    "dependencies": [      {        "uuid": "Четвёртый уникальный id",        "version": [ 0, 1, 0 ]      },      {        "uuid": "Пятый уникальный id",              "version": [ 0, 1, 0 ]      }    ]  }

Если хотите, пробелы можете убрать. Вы наверное заметили, что здесь используется 5 уникальных айди. Это может быть для кого-то странно. Также мы указали путь к индексу. Вы уже наверное понимаете структуру.

В папке аддона создадим папку scripts, в ней хранятся скрипты (тоже очевидно). Создадим в папке scripts папку main. В ней файл index.js, языка javascript. Это наш индекс. Введите туда этот код

import { chatrank } from './misc/chat.js' import { world } from 'mojang-minecraft' import { timer } from './misc/second.js' let tick = 0, worldLoaded = false, loadTime = 0;  world.events.beforeChat.subscribe((data) => {     chatrank(data) }) world.events.tick.subscribe((ticks) => {     tick++     if (!world.getDimension("overworld").runCommand('testfor @a').error && !worldLoaded) {         loadTime = tick         worldLoaded = true;         world.getDimension("overworld").runCommand(`execute @r ~~~ say §l§aМир был загружен в ${ticks} тиках. Добро пожаловать! `)         world.getDimension("overworld").runCommand(`scoreboard objectives add chatsSent dummy`)     }     if(tick >= 20){         tick = 0         timer()     } })

Думаю, вы поняли, что это отображение того, сколько тиках загружался мир. Ну, базовых знаний js достаточно, чтобы тут не объяснять. Далее в папке main создадим папку misc, а в ней файл chat.js, это будет отвечать за спам и ранги.

import { world } from "mojang-minecraft"  let messages = new Map()  function chatrank(data){     const tags = data.sender.getTags()     data.sender.runCommand(`scoreboard players add @s chatsSent 0`)     let score = parseInt(data.sender.runCommand(`scoreboard players test @s chatsSent *`).statusMessage.match(/-?\d+/)[0])     let ranks = [];     for(const tag of tags){         if(tag.startsWith('rank:')){             ranks.push(tag.replace('rank:', ''))         }     }     if(ranks.length == 0)ranks = ["§l§aPlayer"]          if(data.message.startsWith("!*")){         data.cancel = true         return     }     if(score >= 3){         data.cancel = true         return world.getDimension("overworld").runCommand(`ability "${data.sender.nameTag}" mute true`)     }     if(!messages.get(data.sender.name)){         messages.set(data.sender.name, data.message)     }else {         const oldMsg = messages.get(data.sender.name)         if(oldMsg == data.message){             data.cancel = true             return world.getDimension("overworld").runCommand(`tellraw "${data.sender.nameTag}" {"rawtext":[{"text":"§l§cНе пишите похожие сообщения"}]}`)         }     }     let text = `§f[${ranks}§r§f] §7${data.sender.nameTag}: §f${data.message}`     world.getDimension('overworld').runCommand(`tellraw @a {"rawtext":[{"translate":"§l§eM§r - ${JSON.stringify(text)}}]}`)     messages.set(data.sender.name, data.message)     data.sender.runCommand(`scoreboard players add @s chatsSent 1`)     data.cancel = true } export { chatrank }

Чтобы создать себе ранг, нужно вписать tag @s add rank:ВАШ_РАНГ. Например, rank:§l§cADMIN, это будет красным ADMIN, а по умолчанию ранг зелёным Player. Но пока что всё равно ранги и анти-спам не будут работать, нужно в misc создать ещё один файл second.js, по названию всё понятно.

import { world } from 'mojang-minecraft' let seconds = 0  export function timer(){     seconds++     if(seconds >= 4){         world.getDimension("overworld").runCommand(`scoreboard players reset * chatsSent`)         world.getDimension("overworld").runCommand(`scoreboard players set "dummy" chatsSent 1`)         seconds = 0         return seconds     } }

Это тоже небольшой js код, который импортирует секунды.Теперь заходим в майнкрафт, создаём мир с этим аддоном, в настройках мира включаем gametest и education edition. Внимание: поддерживаемая версия рангов и антиспама — 1.19.11 и выше. На других, более старых версиях, есть небольшой баг с командой tellraw.

Если что, у меня пустой ник, там вместо >> будет ":". Перед >> ник, но у меня пустой» title=»Если что, у меня пустой ник, там вместо >> будет ":". Перед >> ник, но у меня пустой» width=»2340″ height=»1080″ data-src=»https://habrastorage.org/getpro/habr/upload_files/260/073/103/26007310354b975331f5e9bd2d2f4f68.jpg» data-blurred=»true»/><figcaption>Если что, у меня пустой ник, там вместо >> будет «:». Перед >> ник, но у меня пустой</figcaption></figure>
<p>Далее — кастомные команды в чате. Это звучит необычно, но, если вы в чат например ввели     .mycommand, выполнится команда. Она будет доступна всем игрокам. Создайте в папке scripts файл commands.js</p>
<pre><code class=import { world } from "mojang-minecraft"; world.events.beforeChat.subscribe((eventData) => { var player_name = eventData.sender.name var player = eventData.sender var args = eventData.message.split(" ") if (args[0].charAt(0) == ".") { eventData.cancel = true if (args[0] == ".mycommand") { player.runCommand("здесь первая команда") player.runCommand("здесь другая команда. Копируйте эти строки бесконечно") } else { player.runCommand("tellraw @s {\"rawtext\":[{\"text\":\"§cНеизвестная команда\"}]}"); } } });

Вы можете указать много команд в строке player.runCommand(«команда»). Далее, необходимо создать файл main.js

import 'scripts/commands.js'

Всего лишь одна строка, импортирующая скрипт. Давайте отредактируем команды в прошлом коде и в майнкрафте введём в чат .mycommand, выполнятся введённые команды.

Как поставить авторские права на аддон? Легко. Если аддон не был скачан, вы можете создать в нём файл LICENSE.md или LICENSE.txt, в них всё указать. Вот пример

this addon by @DinoZavr2 AntiSpam and chat rangs  ©2022

Лицензия может быть очень длинная, всё на ваш вкус.

Текстуры

Мы только что говорили о аддонах. Теперь поговорим о текстурах. Создадим папку mytextures по пути android/data/com.mojang…/files/games/com.mojang/resource_packs, опять же название папки не влияет на название ресурспака. В папке ресурспака создадим наш manifest.json. Вот собственно его код

{   "format_version": 1,   "header": {     "description": "описание вашего текстурпака",     "name": "название вашего текстурпака",     "uuid": "уникальный айди текстурпака",     "version": [       0,       0,       1     ],     "min_engine_version": [       1,       8,       0     ]   },   "modules": [     {       "description": "",       "type": "resources",       "uuid": "второй уникальный айди текстурпака",       "version": [         0,         0,         1       ]     }   ] }

Опять же, как и в аддоне, уникальный айди можно сгенерировать на любом сайте, например, uuidgener*tor.net. Вместо «а» я также поставил «*», чтобы ничего не нарушать. Для создания иконки создайте файл pack_icon.jpg или pack_icon.png.

Чтобы создать текстуры блоков, нужно в папке текстурпака создать папку textures. В ней папку blocks. В blocks запихиваем файл diamond_block.jpg (или .png). Теперь в игре алмазный блок будет выглядеть так, как в diamond_block.jpg.

Чтобы создать текстуры предметов, которые у вас в руках, необходимо в textures создать папку items. В неё запихивайте item.jpg, тоесть у меня например stick.jpg, в игре палка будет выглядеть по другому, тоесть так, как выглядит изображение stick.png.

Субпаки. Subpacks — это паки, использующие json код, который что-то определяет. Например, так можно сделать отображение хитбоксов игрока. Вот манифест для субпаков

{    "format_version":2,    "header":{       "description":"описание текстурпака",       "name":"название текстурпака",        "uuid":"уникальный id",       "version":[1,0,0],       "min_engine_version":[1,14,0]    },    "modules":[       {          "description":"",          "type":"resources",          "uuid":"второй уникальный id",          "version":[1,0,0]       }    ],    "subpacks":[       {          "folder_name":"no_ray",          "name":"",          "memory_tier":1       },       {          "folder_name":"opaque_model_collision",          "name":"",          "memory_tier":2       },       {          "folder_name":"default",          "name":"",          "memory_tier":3       }    ] }

По манифесту видно, что будет очень сложно. Но всё таки придётся сделать отображение хитбоксов.

Создайте в папке текстурпака папка subpacks. В ней ещё три папки — default, no_ray, opaque_model_collision. Сразу говорю, если вы не знакомы с субпаками и json, ознакомьтесь с ними, потому что сдесь вам без базовых знаний о субпаках вам не будет понятно. Дальше в папке default создаём папку animations, в которой содержатся все анимации. В ней hitboxrot.json. В ней отредактируем код анимации

{ "format_version" : "1.8.0", "animations" : { "animation.player.hitboxrot" : { "loop" : true, "bones" : { "hitbox" : { "rotation" : [ 0.0, "-query.body_y_rotation", 0.0 ] } } }, "animation.player.rayrot" : { "loop" : true, "bones" : { "ray" : { "rotation" : [ "query.is_sneaking ? query.target_x_rotation - 1.5 : query.target_x_rotation", "query.target_y_rotation", 0.0 ], "position" : [ 0.0, "query.is_sneaking ? -4.25 : 0.0", 0.0 ] } } }, "animation.player.axis" : { "loop" : true, "bones" : { "axis" : { "rotation" : [ 0.0, "-query.body_y_rotation", 0.0 ] } } } } }

Небольшой код в 30 строк для анимаций. Теперь если игрок подвижится, текстуры будут с ним поворачиваться. Далее в subpack/default создаём папку entity, естественно, там сущности, на которых действуют текстуры. В entity необходимо создать файл player.entity.json, это сущность игрока. Вот код

{   "format_version": "1.10.0",   "minecraft:client_entity": {     "description": {       "identifier": "minecraft:player",       "materials": {         "default": "entity_alphatest",         "cape": "entity_alphatest",         "animated": "player_animated",         "emissive": "entity_emissive_alpha_hitbox"       },       "textures": {         "default": "textures/entity/steve",         "cape": "textures/entity/cape_invisible",         "hitbox": "textures/models/hitboxoverlay",         "ray": "textures/models/ray"       },       "geometry": {         "default": "geometry.humanoid.custom",         "cape": "geometry.cape",         "hitbox": "geometry.hitbox",         "hitbox.swimming": "geometry.hitbox.swimming",         "ray": "geometry.ray"       },       "scripts": {         "scale": "0.9375",         "initialize": [           "variable.is_holding_right = 0.0;",           "variable.is_blinking = 0.0;",           "variable.last_blink_time = 0.0;",           "variable.hand_bob = 0.0;"         ],         "pre_animation": [           "variable.helmet_layer_visible = 1.0;",           "variable.leg_layer_visible = 1.0;",           "variable.boot_layer_visible = 1.0;",           "variable.chest_layer_visible = 1.0;",           "variable.attack_body_rot_y = Math.sin(360*Math.sqrt(variable.attack_time)) * 5.0;",           "variable.tcos0 = (math.cos(query.modified_distance_moved * 38.17) * query.modified_move_speed / variable.gliding_speed_value) * 57.3;",           "variable.first_person_rotation_factor = math.sin((1 - variable.attack_time) * 180.0);",           "variable.hand_bob = query.life_time < 0.01 ? 0.0 : variable.hand_bob + ((query.is_on_ground && query.is_alive ? math.clamp(math.sqrt(math.pow(query.position_delta(0), 2.0) + math.pow(query.position_delta(2), 2.0)), 0.0, 0.1) : 0.0) - variable.hand_bob) * 0.02;",            "variable.map_angle = math.clamp(1 - variable.player_x_rotation / 45.1, 0.0, 1.0);",           "variable.item_use_normalized = query.main_hand_item_use_duration / query.main_hand_item_max_duration;"         ],         "animate": [           "root",           "hitbox_rot",           "ray_rot",           "axis"         ]       },       "animations": {       "hitbox_rot": "animation.player.hitboxrot",       "ray_rot": "animation.player.rayrot",       "axis": "animation.player.axis",         "root": "controller.animation.player.root",         "base_controller": "controller.animation.player.base",         "hudplayer":  "controller.animation.player.hudplayer",         "humanoid_base_pose": "animation.humanoid.base_pose",         "look_at_target": "controller.animation.humanoid.look_at_target",         "look_at_target_ui": "animation.player.look_at_target.ui",         "look_at_target_default": "animation.humanoid.look_at_target.default",         "look_at_target_gliding": "animation.humanoid.look_at_target.gliding",         "look_at_target_swimming": "animation.humanoid.look_at_target.swimming",         "look_at_target_inverted": "animation.player.look_at_target.inverted",         "cape": "animation.player.cape",         "move.arms": "animation.player.move.arms",         "move.legs": "animation.player.move.legs",         "swimming": "animation.player.swim",         "swimming.legs": "animation.player.swim.legs",         "riding.arms": "animation.player.riding.arms",         "riding.legs": "animation.player.riding.legs",         "holding": "animation.player.holding",         "brandish_spear": "animation.humanoid.brandish_spear",         "charging": "animation.humanoid.charging",         "attack.positions": "animation.player.attack.positions",         "attack.rotations": "animation.player.attack.rotations",         "sneaking": "animation.player.sneaking",         "bob": "animation.player.bob",         "damage_nearby_mobs": "animation.humanoid.damage_nearby_mobs",         "bow_and_arrow": "animation.humanoid.bow_and_arrow",         "fishing_rod": "animation.humanoid.fishing_rod",         "use_item_progress": "animation.humanoid.use_item_progress",         "skeleton_attack": "animation.skeleton.attack",         "sleeping": "animation.player.sleeping",         "first_person_base_pose": "animation.player.first_person.base_pose",         "first_person_empty_hand": "animation.player.first_person.empty_hand",         "first_person_swap_item": "animation.player.first_person.swap_item",         "first_person_attack_controller": "controller.animation.player.first_person_attack",         "first_person_attack_rotation": "animation.player.first_person.attack_rotation",         "first_person_vr_attack_rotation": "animation.player.first_person.vr_attack_rotation",         "first_person_walk": "animation.player.first_person.walk",         "first_person_map_controller": "controller.animation.player.first_person_map",         "first_person_map_hold": "animation.player.first_person.map_hold",         "first_person_map_hold_attack": "animation.player.first_person.map_hold_attack",         "first_person_map_hold_off_hand": "animation.player.first_person.map_hold_off_hand",         "first_person_map_hold_main_hand": "animation.player.first_person.map_hold_main_hand",         "first_person_crossbow_equipped": "animation.player.first_person.crossbow_equipped",         "third_person_crossbow_equipped": "animation.player.crossbow_equipped",         "third_person_bow_equipped": "animation.player.bow_equipped",         "crossbow_hold": "animation.player.crossbow_hold",         "crossbow_controller": "controller.animation.player.crossbow",         "shield_block_main_hand": "animation.player.shield_block_main_hand",         "shield_block_off_hand": "animation.player.shield_block_off_hand",         "blink": "controller.animation.persona.blink"       },       "render_controllers": [         { "controller.render.player.first_person": "variable.is_first_person" },         { "controller.render.player.third_person": "!variable.is_first_person && !variable.map_face_icon" },         { "controller.render.player.map": "variable.map_face_icon" },         { "controller.render.player.hitbox": "!variable.is_first_person && !query.is_in_ui" },         { "controller.render.player.ray": "!variable.is_first_person && !query.is_in_ui" }       ],       "enable_attachables": true     }   } }

Это очень сложно понять без хотя бы базовых знаний json. Тут применяются математические примеры, рендер контроллеры, целых 118 строк. rotation’ы и всё подобное. Можете создать много сущностей для отображения хитбоксов, например, для иссушителя или волка. Но лучше не надо, а то если на многих сущностях будут действовать текстуры, будет лагать. Далее нам нужны материалы — создаём в default папку materials. В ней entity.material (без json), расширение material.

{   "materials": {     "version": "1.0.0", "entity_emissive_alpha_hitbox:entity_nocull": {       "+defines": [         "ALPHA_TEST",         "USE_EMISSIVE"       ],       "depthFunc": "Always"     }   } }

Думаю, здесь объяснять ничего не надо. Функция будет работать всегда. Дальше ещё сложнее — в default создайте папку models, в ней entity. А в entity 3 файла — первый hitbox.json

{ "format_version": "1.10.0", "geometry.hitbox": { "texturewidth": 64, "textureheight": 64, "visible_bounds_width": 3, "visible_bounds_height": 3, "visible_bounds_offset": [0, 1.5, 0], "bones": [ { "name": "hitbox", "pivot": [0, 0, 0], "cubes": [ { "origin": [-5, 0.2, -5], "size": [10, 30, 10], "uv": [0, 0], "inflate": 0.3 }, { "origin": [-4.5, 0.5, -4.5], "size": [9, 9, 9], "uv": [0, 42] } ] } ] } }

В этом файле мы указали размер хитбокса и сами хитбоксы. Второй файл — ray.json

{ "format_version": "1.12.0", "minecraft:geometry": [ { "description": { "identifier": "geometry.ray", "texture_width": 16, "texture_height": 16, "visible_bounds_width": 2, "visible_bounds_height": 2, "visible_bounds_offset": [0, 2, 0] }, "bones": [ { "name": "ray", "pivot": [0, 27, 0], "cubes": [ {"origin": [-0.5, 26.5, -0.5], "size": [1, 1, -16], "inflate": -0.41, "uv": [0, 0]} ] } ] } ] }

Здесь тоже, думаю, объяснять ничего не надо — мы создали рэй. И третий файл — hitboxhitbox_swimming.json

{ "format_version": "1.10.0", "geometry.hitbox.swimming": { "texturewidth": 64, "textureheight": 64, "visible_bounds_width": 3, "visible_bounds_height": 3, "visible_bounds_offset": [0, 1.5, 0], "bones": [ { "name": "hitbox", "pivot": [0, 0, 0], "cubes": [ { "origin": [-5, 0.2, -5], "size": [10, 9, 10], "uv": [0, 0], "inflate": 0.3 }, { "origin": [-4.5, 0.5, -4.5], "size": [9, 9, 9], "uv": [0, 42] } ] } ] } }

Здесь мы указали отображение хитбоксов в режиме плавания. Это не будет выглядеть странно, как без этого файла. Если этого файла не будет, при плавании у игрока будет хитбокс, как будто он стоя ходит.

Теперь возвращаемся в папку default. В ней нужно создать папку render_controllers — в ней тоже hitbox.json и ray.json. Вот hitbox.json

{   "format_version": "1.8.0",   "render_controllers": {     "controller.render.player.hitbox": {       "geometry": "Array.geo[query.is_swimming]",       "materials": [ { "*": "Material.emissive" } ],       "textures": [ "Texture.hitbox" ],       "arrays": {         "geometries": { "Array.geo": [ "geometry.hitbox", "geometry.hitbox.swimming"] }       },       "is_hurt_color":{},       "on_fire_color":{}     }   } }

Тут мы также не забыли указать про плавание. Теперь ray.json

{   "format_version": "1.8.0",   "render_controllers": {     "controller.render.player.ray": {       "geometry": "Geometry.ray",       "materials": [ { "*": "Material.emissive" } ],       "textures": [ "Texture.ray" ],       "is_hurt_color":{},       "on_fire_color":{},       "part_visibility": [         { "*": "!query.is_swimming" }       ]     }   } }

Да да, опять указали swimming. Это рендер контроллеры. И создаём последнюю папку в папке default — textures. Как же без текстур хитбокса? Закидываем туда models/hitboxoverlay.png,ray.png

Вот вам hitboxoverlay

hitboxoverlay.png
hitboxoverlay.png

И вот ray.png

ray.png
ray.png

Да, они находятся в models. Вы, наверное, не видите тут модели, но они есть. На этом с папкой default мы закончили

Ну что, с папкой default закончили. В ней мы указали модели, материалы, плаванье и отображение хитбоксов, также модели хитбоксов. Теперь переходим в папку subpacks, в которой и содержался default. В subpacks создаём папку no_ray. Вы наверное уже представляете, насколько это будет долго. В no_ray создадим несколько папок — animations, entity, materials, models, render_controllers, textures.

В папке animations создайте файл hitboxrot.json

{ "format_version" : "1.8.0", "animations" : { "animation.player.hitboxrot" : { "loop" : true, "bones" : { "hitbox" : { "rotation" : [ 0.0, "-query.body_y_rotation", 0.0 ] } } }, "animation.player.axis" : { "loop" : true, "bones" : { "axis" : { "rotation" : [ 0.0, "-query.body_y_rotation", 0.0 ] } } } } }

hitboxrot — hitboxrotation. Это небольшое сокращение, но файл называется именно hitboxrot.json. Тут есть body_rotation и всё в этом роде, тоесть анимации без ray, это подтверждает папка no_ray. Далее в no_ray в materials создайте папку entity.material, тоесть с расширением material.

{   "materials": {     "version": "1.0.0", "entity_emissive_alpha_hitbox:entity_nocull": {       "+defines": [         "ALPHA_TEST",         "USE_EMISSIVE"       ],       "depthFunc": "Always"     }   } }

Опять анимации в материалах. Ладно, дальше в models создаём папку entity, в которой hitbox_swimming.json и hitbox.json. Вот hitbox.json

{ "format_version": "1.10.0", "geometry.hitbox": { "texturewidth": 64, "textureheight": 64, "visible_bounds_width": 3, "visible_bounds_height": 3, "visible_bounds_offset": [0, 1.5, 0], "bones": [ { "name": "hitbox", "pivot": [0, 0, 0], "cubes": [ { "origin": [-5, 0.2, -5], "size": [10, 30, 10], "uv": [0, 0], "inflate": 0.3 }, { "origin": [-4.5, 0.5, -4.5], "size": [9, 9, 9], "uv": [0, 42] } ] } ] } }

Здесь размер хитбокса в json. Дальше hitbox_swimming.json — плавающий хитбокс

{ "format_version": "1.10.0", "geometry.hitbox.swimming": { "texturewidth": 64, "textureheight": 64, "visible_bounds_width": 3, "visible_bounds_height": 3, "visible_bounds_offset": [0, 1.5, 0], "bones": [ { "name": "hitbox", "pivot": [0, 0, 0], "cubes": [ { "origin": [-5, 0.2, -5], "size": [10, 9, 10], "uv": [0, 0], "inflate": 0.3 }, { "origin": [-4.5, 0.5, -4.5], "size": [9, 9, 9], "uv": [0, 42] } ] } ] } }

Здесь хитбокс в режиме плавания. Кого не смущает стоячий хитбокс плавающего игрока? Ладно, вот hitbox.json в render_controllers

{   "format_version": "1.8.0",   "render_controllers": {     "controller.render.player.hitbox": {       "geometry": "Array.geo[query.is_swimming]",       "materials": [ { "*": "Material.emissive" } ],       "textures": [ "Texture.hitbox" ],       "arrays": {         "geometries": { "Array.geo": [ "geometry.hitbox", "geometry.hitbox.swimming"] }       },       "is_hurt_color":{},       "on_fire_color":{}     }   } }

Дальше в папке no_ray/textures/models вставляем hitboxoverlay.png, как же без него

hitboxoverlay.png
hitboxoverlay.png

На этом с no_ray мы закончили. Но это ещё не всё — в субпаках есть папка opaque_model_collision. Напоминаю, мы делаем текстурпак с хитбоксами только для ознакомления с субпаками и json. Теперь, в opaque_model_collision создаём папки animations, entity, models, render_controllers, textures. Давайте немного от редактируем -сначалa папка animations. В ней hitboxrot.json. Вот hitboxrot.json

{ "format_version" : "1.8.0", "animations" : { "animation.player.hitboxrot" : { "loop" : true, "bones" : { "hitbox" : { "rotation" : [ 0.0, "-query.body_y_rotation", 0.0 ] } } }, "animation.player.rayrot" : { "loop" : true, "bones" : { "ray" : { "rotation" : [ "query.is_sneaking ? query.target_x_rotation - 1.5 : query.target_x_rotation", "query.target_y_rotation", 0.0 ], "position" : [ 0.0, "query.is_sneaking ? -4.25 : 0.0", 0.0 ] } } }, "animation.player.axis" : { "loop" : true, "bones" : { "axis" : { "rotation" : [ 0.0, "-query.body_y_rotation", 0.0 ] } } } } }

Мы указали кружение объекта, луп и хитбоксы. Далее указываем сущности — entity/player.entity.json

{   "format_version": "1.10.0",   "minecraft:client_entity": {     "description": {       "identifier": "minecraft:player",       "materials": {         "default": "entity_alphatest",         "cape": "entity_alphatest",         "animated": "player_animated",         "emissive": "entity_emissive_alpha"       },       "textures": {         "default": "textures/entity/steve",         "cape": "textures/entity/cape_invisible",         "hitbox": "textures/models/hitboxoverlay",         "ray": "textures/models/ray"       },       "geometry": {         "default": "geometry.humanoid.custom",         "cape": "geometry.cape",         "hitbox": "geometry.hitbox",         "hitbox.swimming": "geometry.hitbox.swimming",         "ray": "geometry.ray"       },       "scripts": {         "scale": "0.9375",         "initialize": [           "variable.is_holding_right = 0.0;",           "variable.is_blinking = 0.0;",           "variable.last_blink_time = 0.0;",           "variable.hand_bob = 0.0;"         ],         "pre_animation": [           "variable.helmet_layer_visible = 1.0;",           "variable.leg_layer_visible = 1.0;",           "variable.boot_layer_visible = 1.0;",           "variable.chest_layer_visible = 1.0;",           "variable.attack_body_rot_y = Math.sin(360*Math.sqrt(variable.attack_time)) * 5.0;",           "variable.tcos0 = (math.cos(query.modified_distance_moved * 38.17) * query.modified_move_speed / variable.gliding_speed_value) * 57.3;",           "variable.first_person_rotation_factor = math.sin((1 - variable.attack_time) * 180.0);",           "variable.hand_bob = query.life_time < 0.01 ? 0.0 : variable.hand_bob + ((query.is_on_ground && query.is_alive ? math.clamp(math.sqrt(math.pow(query.position_delta(0), 2.0) + math.pow(query.position_delta(2), 2.0)), 0.0, 0.1) : 0.0) - variable.hand_bob) * 0.02;",            "variable.map_angle = math.clamp(1 - variable.player_x_rotation / 45.1, 0.0, 1.0);",           "variable.item_use_normalized = query.main_hand_item_use_duration / query.main_hand_item_max_duration;"         ],         "animate": [           "root",           "hitbox_rot",           "ray_rot",           "axis"         ]       },       "animations": {       "hitbox_rot": "animation.player.hitboxrot",       "ray_rot": "animation.player.rayrot",       "axis": "animation.player.axis",         "root": "controller.animation.player.root",         "base_controller": "controller.animation.player.base",         "hudplayer":  "controller.animation.player.hudplayer",         "humanoid_base_pose": "animation.humanoid.base_pose",         "look_at_target": "controller.animation.humanoid.look_at_target",         "look_at_target_ui": "animation.player.look_at_target.ui",         "look_at_target_default": "animation.humanoid.look_at_target.default",         "look_at_target_gliding": "animation.humanoid.look_at_target.gliding",         "look_at_target_swimming": "animation.humanoid.look_at_target.swimming",         "look_at_target_inverted": "animation.player.look_at_target.inverted",         "cape": "animation.player.cape",         "move.arms": "animation.player.move.arms",         "move.legs": "animation.player.move.legs",         "swimming": "animation.player.swim",         "swimming.legs": "animation.player.swim.legs",         "riding.arms": "animation.player.riding.arms",         "riding.legs": "animation.player.riding.legs",         "holding": "animation.player.holding",         "brandish_spear": "animation.humanoid.brandish_spear",         "charging": "animation.humanoid.charging",         "attack.positions": "animation.player.attack.positions",         "attack.rotations": "animation.player.attack.rotations",         "sneaking": "animation.player.sneaking",         "bob": "animation.player.bob",         "damage_nearby_mobs": "animation.humanoid.damage_nearby_mobs",         "bow_and_arrow": "animation.humanoid.bow_and_arrow",         "fishing_rod": "animation.humanoid.fishing_rod",         "use_item_progress": "animation.humanoid.use_item_progress",         "skeleton_attack": "animation.skeleton.attack",         "sleeping": "animation.player.sleeping",         "first_person_base_pose": "animation.player.first_person.base_pose",         "first_person_empty_hand": "animation.player.first_person.empty_hand",         "first_person_swap_item": "animation.player.first_person.swap_item",         "first_person_attack_controller": "controller.animation.player.first_person_attack",         "first_person_attack_rotation": "animation.player.first_person.attack_rotation",         "first_person_vr_attack_rotation": "animation.player.first_person.vr_attack_rotation",         "first_person_walk": "animation.player.first_person.walk",         "first_person_map_controller": "controller.animation.player.first_person_map",         "first_person_map_hold": "animation.player.first_person.map_hold",         "first_person_map_hold_attack": "animation.player.first_person.map_hold_attack",         "first_person_map_hold_off_hand": "animation.player.first_person.map_hold_off_hand",         "first_person_map_hold_main_hand": "animation.player.first_person.map_hold_main_hand",         "first_person_crossbow_equipped": "animation.player.first_person.crossbow_equipped",         "third_person_crossbow_equipped": "animation.player.crossbow_equipped",         "third_person_bow_equipped": "animation.player.bow_equipped",         "crossbow_hold": "animation.player.crossbow_hold",         "crossbow_controller": "controller.animation.player.crossbow",         "shield_block_main_hand": "animation.player.shield_block_main_hand",         "shield_block_off_hand": "animation.player.shield_block_off_hand",         "blink": "controller.animation.persona.blink"       },       "render_controllers": [         { "controller.render.player.first_person": "variable.is_first_person" },         { "controller.render.player.third_person": "!variable.is_first_person && !variable.map_face_icon" },         { "controller.render.player.map": "variable.map_face_icon" },         { "controller.render.player.hitbox": "!variable.is_first_person && !query.is_in_ui" },         { "controller.render.player.ray": "!variable.is_first_person && !query.is_in_ui" }       ],       "enable_attachables": true     }   } }

Ууу, тоже 118 строк. Одни вариаблы и контроллеры. Ну ладно, дальше в папке models создайте папку entity. В ней hitbox.json, hitbox_swimming.json, ray.json, вот hitbox

{ "format_version": "1.10.0", "geometry.hitbox": { "texturewidth": 64, "textureheight": 64, "visible_bounds_width": 3, "visible_bounds_height": 3, "visible_bounds_offset": [0, 1.5, 0], "bones": [ { "name": "hitbox", "pivot": [0, 0, 0], "cubes": [ { "origin": [-5, 0.2, -5], "size": [10, 30, 10], "uv": [0, 0], "inflate": 0.3 }, { "origin": [-4.5, 0.5, -4.5], "size": [9, 9, 9], "uv": [0, 42] } ] } ] } }

Указали размер. Теперь в плаванью хитбокса — hitbox_swimming.json

{ "format_version": "1.10.0", "geometry.hitbox.swimming": { "texturewidth": 64, "textureheight": 64, "visible_bounds_width": 3, "visible_bounds_height": 3, "visible_bounds_offset": [0, 1.5, 0], "bones": [ { "name": "hitbox", "pivot": [0, 0, 0], "cubes": [ { "origin": [-5, 0.2, -5], "size": [10, 9, 10], "uv": [0, 0], "inflate": 0.3 }, { "origin": [-4.5, 0.5, -4.5], "size": [9, 9, 9], "uv": [0, 42] } ] } ] } }

ЗдесьЗдесь используется функция geometry.hitbox.swimming, остался ray.json

{ "format_version": "1.12.0", "minecraft:geometry": [ { "description": { "identifier": "geometry.ray", "texture_width": 16, "texture_height": 16, "visible_bounds_width": 2, "visible_bounds_height": 2, "visible_bounds_offset": [0, 2, 0] }, "bones": [ { "name": "ray", "pivot": [0, 27, 0], "cubes": [ {"origin": [-0.5, 26.5, -0.5], "size": [1, 1, -16], "inflate": -0.41, "uv": [0, 0]} ] } ] } ] }

А здесь функция minecraft:geometry. Странно, но всё же так. Далее переходим в render_controllers, там 2 файла — hitbox.json и ray.json. Отредактируем hitbox.json

{   "format_version": "1.8.0",   "render_controllers": {     "controller.render.player.hitbox": {       "geometry": "Array.geo[query.is_swimming]",       "materials": [ { "*": "Material.emissive" } ],       "textures": [ "Texture.hitbox" ],       "arrays": {         "geometries": { "Array.geo": [ "geometry.hitbox", "geometry.hitbox.swimming"] }       },       "is_hurt_color":{},       "on_fire_color":{}     }   } }

Здесь используется функция render_controllers. Дальше ray.json

{   "format_version": "1.8.0",   "render_controllers": {     "controller.render.player.ray": {       "geometry": "Geometry.ray",       "materials": [ { "*": "Material.emissive" } ],       "textures": [ "Texture.ray" ],       "is_hurt_color":{},       "on_fire_color":{},       "part_visibility": [         { "*": "!query.is_swimming" }       ]     }   } }

И опять функция render_controllers. Ладно, что нам осталось? Правильно, textures, в котором models. В models опять 2 файла — hitboxoverlay.png, ray.png. Вот hitboxoverlay.png

�PNG      IHDR         \r�f   �zTXtRaw profile type exif  x��Z[r�:��*f$@�r�����A�v�ĉ�s�߉+�MI|t7�e������/&N��TRr�%�����W��wa�_���~WO׏�RP�q@�yUE}�]p�÷�z���gC���(�z���� Q�G�gCeR��q��l��'�!�Mr�� J#�#a����������n�` ��9�#,�l��M��G���>��|�g�|�2]XK���S�\���uD|��_A^k��1�M���:�����e_��R�G|��*xeW]GW�u���x��|��W���e�C<YQ2w�]�E�pK���`���$T�u,~�[v�g�<<Ne��<.y����?A�]�b�q���0��g��N������@����aΘ`u�h�EӖl��E�Ǟt�  "�1/`�%/�'�Y��U��%p>F$�Ĥ����5���9�U#7��(I�� +��h��P�C�1E��b�5I )��4Y��*4jRլEk�r�)kι�Z�r`,�hɥ�Z�*:�h������MZh���-��j�|z豧�=����!ib��#�2��4�)f�q��3�2�֖���JKW^e�+k'�_^/��O�x3e��5Ԓ�o�$g`���j@�l���C`c�8s��1�h����(�s\��ݍ��x����c����9u_y���a>�7cG�N}8>s%��L�ek}��yH������6��Ec��������&���v�?,��}b�C���g� =6W�P�:��̚'���J���S";ur�V�0WO�����ZY|i�H�6o44��MQJgSM�{��zh��� H�3�sƴB[��{�H�%x?K��=�٣���S[�3h�n ����S9{��z �@����jF�h� ׾]���z�wV����}]��'$�>p�p�F���5d���� ���!�;@��rV<Y��Yh4Y���eQ*�5 ]�3��`�²��D���?�…S��)|��#�'���-�?+�"�kqtHC��c��u �ǂ��b���8��u�K�}���0�:T�7R�{����&�����hcℐZ��07���[n-������MЕs�/�{��2�O��=��CO+�O"�F�I$o8]+w� ���>�cp���-^�h�u F�<ה�]�:�K�:�GRV��V\�gU��{�##p����W[d�����B�`y"V†�1�B��i�aڭԱ.���kPo�� c�%��y�l��*�Y�ˑE���EyZ k�TK�ő`H;��0s�-����C �+��c�CGz�EW� 3 ��M1I���*�S1um[0*w���i� �_m��B��f��y�7u4���v�cy��c�'ޱ�&x])��{lk��pA�B��1�uv��L�_ƛYM�pֹ8l�X���I�`�n5��5���|25h ���z;o`��� ���G?Ȅ��ɏ2�w�ٴ�s1O�E�l���Q&H�]����j�mR��.=i�s�O��u�)>����� �=X��Rb0~���g�=��'���a�H��kҟ=?ZcXߑ��[c������m��W�� [v��]m�օ���9�Y�'c��Xj.�vZ����/6I�On��?���(�HOX�c�?�~�=��Q���G�z�{P�}T�둎�O苞��w$�9(G����w$�!�/��G�z�{P�}T�A�G{U_�g�zC��,To�?�[K�r�>����=(�>*�����܃B�&���� ��_��k� �A��9����-~N�x�|n������M�r��m�%�{|)��Ǯ�h��������G.8��?o����������vG�{����׷7�]�V�=��mʏ!�M�S�~ܻ�#����Ql[H�co7�4�f�)�.����J8�J��fG�νR�ĽW*��+���^��c��k�{�:zMv�U�Zi�UG�����5�=V�&�Ǫ��d�Xu��즻��J��E*N��z���c�%��˂<%ѧ��&�Z�brõ��mW�e�(c�0^m�WBŷF�>B[�Ǥ���`�� k�fY�Ft��n�tŶ:�o�[�yl}�`Jk��=�1U��>���Ț���56����˜���;T0rK��z)C�%B�c�^EdK � !@��Yi� ��������P({z]{(�����w�����|b\%�8B���!�=d�)��%}��e�4�6��5d���4+�85�K�4۵��p���'H��C�)~�`��Ӽ��~�0rG�0�E����˷Xn̄� i���S���l�p#�`�HD�r)HJ+�9Fn�l��PC��#���x���O}[����\, -(�>eב�>|������q���ѯ���(DrI8�O5�=�s�ƪ��ŗvV�X��jM|� �y���yԞ��xLț�_�dYQ�Q���eP���~É����R>��3�'t���`O�H7��HD��epnсș;!�"�Op934�0�P"�Ý�ҥ�зǩs,��/{���R�!S��N%$�C�f��i\:CS�&~��/�.n��- ?����Z���%�!���Jt��4�>5���K�eq��.�-�f�y��!<��6� s��p�I����/ �e��(�k:�'��Ê����3��Ҳ��v3NK^s��]\ �G��gd�$L���+C��2)�"�,GWҡ�?�?P=|Q�9��m�1 �r6�BO��F  �iCCPICC profile  x�}�=H�@�_S�R*�P�!Cu� *�(U,���Vh����/hҐ��8 �?�.κ:� �������������"ƃ�~����{�S;@�,#�L��ªxEˆ"*1SOes�_����.γ���9��� �H<�t�"� �ٴt���V��s�q�.H��u��7�e��1r�y��X�a��Y�P���c��Q��wY��Y�5X��������:�$���!��*j��U#�D���aǟ&�L�*9P� �����n��Ԥ�J �/��1 v�vӶ��m�}���+�믷��Oқ]-v�����&��;@�I�ɑ�4�Rx?�o* C�@p������ G]-� ��X���=�=��ۿg:�� E�r��A   bKGD � � �����   pHYs     ��   tIME� ()�R�  �IDATx���;�0EQb��ڼ�G���$�!��Ў��B�FsI��mU�l���9 If��� ^9Y   �      �      �      �      �      �      �      �      �   ����d:� \�l�����@���H2�w��<�~   @     @     @     @      @     @     @     @     @     @     @     @     @    � @     @     @     @     @     @     @     @     @      @     @     @     @     @     @     `w�dZ_��U�        ��ve�Qcu�'����8,�7%9,�    @     @     @     @     @     @     @     @   ˘����   �      �      �      �      �      �      �      �      ������k�Z��^�huN�H                vw��B?R��    IEND�B`�

Либо

hitboxoverlay.png
hitboxoverlay.png

А вот и ray.png

�PNG      IHDR           szz�  �zTXtRaw profile type exif  x��Q��&E�YE��$��r0�sfY~�rWu�$3�|�L�`�%�]����F���BR�\r�8RI�+���+Ŵ���=��{�'&A/׭ս�®<b��j�gط#�/�eF����$�|�)mG���=�zlGm/\��3�i]ݼ/�J]H�O!�����:��:���1N�aue;� /���c|�E��(|V�}���'-����N�~����˝�N�>\}y��c���Ք�h��Ħ�,< ���2��T�m�����=�x�5*�PJԩҠs��RL|��gn,��b\�� 6)�A���D&�;Zqˊ����2����w����mJD�o��ϺF�ܼb����t�h|��Y�)���5��C飶dq�S��'D��v �[��L��)�AG���Y @�ܑ$'����y��3Fk-+g�f�M �����H���c�QCUE��f5��Ek����-�M��X2�lfnŪ�'W�n�^�.�=PK.V��R+��@�*�WX>�H���ÏrԆ�i�i�͚��j�.�D�ݺ���I��Nq�S�|��g9�@� i��Æ�2�MmS��~�mj�H�uvS�5�=\��Nt21N�6��y2�N)�$7����(���N6��$��$�A7�r?�-��7�'ra��/�����;���=���+��F��7�U�P�F�Z�~�o�!�KoGoGoGoGoGoGoG�G�? ��a�9�����m  �iCCPICC profile  x�}�=H�@�_S�R*�P�!Cu� *�(U,���Vh����/hҐ��8 �?�.κ:� �������������"ƃ�~����{�S;@�,#�L��ªxEˆ"*1SOes�_����.γ���9��� �H<�t�"� �ٴt���V��s�q�.H��u��7�e��1r�y��X�a��Y�P���c��Q��wY��Y�5X��������:�$���!��*j��U#�D���aǟ&�L�*9P� �����n��Ԥ�J �/��1 v�vӶ��m�}���+�믷��Oқ]-v�����&��;@�I�ɑ�4�Rx?�o* C�@p������ G]-� ��X���=�=��ۿg:�� E�r��A   bKGD � � �����   pHYs     ��   tIME� *5�<l(   -IDATX���A  �#��=��%X%;y�y&              p��Y4>��    IEND�B`�

Либо прошлые картинки ray.png.

На этом всё. Импортируем аддон и заходим в майнкрафт. Запускаем и проверяем — у всех игроков есть хитбоксы.

На этом всё. Теперь перевод. Чтобы в ресурспаке сделать название предметов американцу stick, а русскому палка-копалка, создайте в папке ресурспака папку texts, в ней ru_RU.lang. Далее строка

item.предмет.name=§l§aПалка копалка

На этом всё. Я рассказал про наборы параметров, ресурспаки и субпаки в текстурпаках, отображение хитбокса, перевод и анти-спам с рангами чата. Надеюсь, это статья была полезной.


ссылка на оригинал статьи https://habr.com/ru/post/701226/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *