Привет, Хабр! В этой статье я хочу затронуть тему аддонов. Многим чего-то не хватало в игре, и они скачивали аддоны с интернета. Когда вы понимаете, что скачанный аддон настолько безполезный, удаляете его. Я, допустим, хочу сделать аддон в 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.
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

И вот 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, как же без него

На этом с 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`�
Либо

А вот и 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/
Добавить комментарий