<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Topics tagged with unity]]></title><description><![CDATA[A list of topics that have been tagged with unity]]></description><link>https://celua.ru/tags/unity</link><generator>RSS for Node</generator><lastBuildDate>Tue, 17 Mar 2026 03:01:22 GMT</lastBuildDate><atom:link href="https://celua.ru/tags/unity.rss" rel="self" type="application/rss+xml"/><pubDate>Invalid Date</pubDate><ttl>60</ttl><item><title><![CDATA[Крафт в StarsOne]]></title><description><![CDATA[В оригинале, если нет всех компонентов рецепта, то нельзя скрафтить вещь.

Цель: скрафтить без компонетов по нажатию кнопки
Игра на Unity и можно пробовать использовать в dnSpy модифицировать файл Assembly-CSharp.dll
Открываем файлик и смотрим на классы связанные с крафтом
Так находим кнопку, которая создаст указанное количество вещей крафта в CreateItem


Задача скрафтить по имеющемуся рецепту любую вещь. Для этого я добавляю проверку количества вещей  и удаляю лишний код. Под сполерами оригинальный и модифицированный код

Модифицированный код
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;

public partial class Crafting : MonoBehaviour
{
	public void CreateItem(int curCraft, int craftCount)
	{
		if (this.timeBreak &gt;= 0.3f)
		{
            // Добавили одну вещь крафта
			if (craftCount == 0)
			{
				craftCount = 1;
			}

			if (craftCount != 0)
			{
				this.succes = true;
				CraftRecipe component = this.craftObjects[curCraft].GetComponent&lt;CraftRecipe&gt;();
              
                // Здесь удалили код, который должен расходовать вещи из инвентаря
                // ...
              
				if (this.succes)
				{
					this.iks = this.craftObjects[curCraft].GetComponent&lt;CraftRecipe&gt;().countCreate * craftCount;
					for (int m = 0; m &lt; this.craftObjects[curCraft].GetComponent&lt;CraftRecipe&gt;().countCreate * craftCount; m++)
					{
						this.emptyCell = Inventory.inv.FindItemForStack(this.craftObjects[curCraft].name);
						if (this.emptyCell &gt;= 0)
						{
							if (Inventory.inv.items[this.emptyCell].stack + this.iks &lt;= Inventory.inv.items[this.emptyCell].maxStack)
							{
								Inventory.inv.items[this.emptyCell].stack += this.iks;
								break;
							}
							this.iks -= Inventory.inv.items[this.emptyCell].maxStack - Inventory.inv.items[this.emptyCell].stack;
							Inventory.inv.items[this.emptyCell].stack = Inventory.inv.items[this.emptyCell].maxStack;
						}
						else
						{
							this.emptyCell = Inventory.inv.FindEmptyCell();
							if (this.emptyCell &gt;= 0)
							{
								this.cloneItem = Inventory.inv.CreateItem(this.craftObjects[curCraft]);
								if (this.cloneItem.maxStack &gt;= this.iks)
								{
									this.cloneItem.stack = this.iks;
									Inventory.inv.items[this.emptyCell] = this.cloneItem;
									break;
								}
								this.cloneItem.stack = this.cloneItem.maxStack;
								this.iks -= this.cloneItem.maxStack;
								Inventory.inv.items[this.emptyCell] = this.cloneItem;
							}
							else
							{
								if (this.craftObjects[curCraft].GetComponent&lt;Item&gt;().maxStack &gt;= this.iks)
								{
									PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.iks, this.craftObjects[curCraft].GetComponent&lt;Item&gt;().health);
									break;
								}
								PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.craftObjects[curCraft].GetComponent&lt;Item&gt;().maxStack, this.craftObjects[curCraft].GetComponent&lt;Item&gt;().health);
								this.iks -= this.craftObjects[curCraft].GetComponent&lt;Item&gt;().maxStack;
							}
							new WaitForEndOfFrame();
						}
					}
				}
			}
			this.timeBreak = 0f;
		}
	}
}

Оригинальный
public void CreateItem(int curCraft, int craftCount)
	{
		if (this.timeBreak &gt;= 0.3f)
		{
			if (craftCount != 0)
			{
				this.succes = true;
				CraftRecipe component = this.craftObjects[curCraft].GetComponent&lt;CraftRecipe&gt;();
				int[] array = new int[7];
				int[] array2 = new int[7];
				for (int i = 0; i &lt; 7; i++)
				{
					if (this.succes &amp;&amp; component.ingredients[i] != null)
					{
						array2[i] = component.countIngredients[i];
						if (array2[i] &lt;= 0)
						{
							array2[i] = 1;
						}
						array2[i] *= craftCount;
						for (int j = 0; j &lt; Inventory.inv.items.Length; j++)
						{
							if (Inventory.inv.items[j] != null &amp;&amp; component.ingredients[i].name == Inventory.inv.items[j].prefName)
							{
								array[i] += Inventory.inv.items[j].stack;
							}
						}
						if (array[i] &lt; array2[i])
						{
							this.succes = false;
						}
					}
				}
				if (this.succes)
				{
					for (int k = 0; k &lt; 7; k++)
					{
						if (component.ingredients[k] != null)
						{
							for (int l = 0; l &lt; Inventory.inv.items.Length; l++)
							{
								if (Inventory.inv.items[l] != null &amp;&amp; array2[k] &gt; 0 &amp;&amp; component.ingredients[k].name == Inventory.inv.items[l].prefName)
								{
									this.iks = Inventory.inv.items[l].stack;
									Inventory.inv.items[l].stack -= array2[k];
									array2[k] -= this.iks;
									if (array2[k] &lt;= 0)
									{
										break;
									}
								}
							}
						}
					}
					this.iks = this.craftObjects[curCraft].GetComponent&lt;CraftRecipe&gt;().countCreate * craftCount;
					for (int m = 0; m &lt; this.craftObjects[curCraft].GetComponent&lt;CraftRecipe&gt;().countCreate * craftCount; m++)
					{
						this.emptyCell = Inventory.inv.FindItemForStack(this.craftObjects[curCraft].name);
						if (this.emptyCell &gt;= 0)
						{
							if (Inventory.inv.items[this.emptyCell].stack + this.iks &lt;= Inventory.inv.items[this.emptyCell].maxStack)
							{
								Inventory.inv.items[this.emptyCell].stack += this.iks;
								break;
							}
							this.iks -= Inventory.inv.items[this.emptyCell].maxStack - Inventory.inv.items[this.emptyCell].stack;
							Inventory.inv.items[this.emptyCell].stack = Inventory.inv.items[this.emptyCell].maxStack;
						}
						else
						{
							this.emptyCell = Inventory.inv.FindEmptyCell();
							if (this.emptyCell &gt;= 0)
							{
								this.cloneItem = Inventory.inv.CreateItem(this.craftObjects[curCraft]);
								if (this.cloneItem.maxStack &gt;= this.iks)
								{
									this.cloneItem.stack = this.iks;
									Inventory.inv.items[this.emptyCell] = this.cloneItem;
									break;
								}
								this.cloneItem.stack = this.cloneItem.maxStack;
								this.iks -= this.cloneItem.maxStack;
								Inventory.inv.items[this.emptyCell] = this.cloneItem;
							}
							else
							{
								if (this.craftObjects[curCraft].GetComponent&lt;Item&gt;().maxStack &gt;= this.iks)
								{
									PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.iks, this.craftObjects[curCraft].GetComponent&lt;Item&gt;().health);
									break;
								}
								PlayerNetwork.inst.CallCmdCreateItem(this.craftObjects[curCraft].name, Inventory.inv.transform.position - Vector3.forward * 2f + FloatingOrigin.offset, Quaternion.identity, this.craftObjects[curCraft].GetComponent&lt;Item&gt;().maxStack, this.craftObjects[curCraft].GetComponent&lt;Item&gt;().health);
								this.iks -= this.craftObjects[curCraft].GetComponent&lt;Item&gt;().maxStack;
							}
							new WaitForEndOfFrame();
						}
					}
				}
			}
			this.timeBreak = 0f;
		}
	}

Изменяем весь класс или метов в этом окне

Если выводит ошибки при компяляции, то скачиваем IlSpy и его код вставляем в код в dnSpy. Или качаем DnSpy 3.2.0 или ранее
Изменения сохраняем в модуль, запускаем игру и крафтим.
Получить все рецепты (не проверял правда, попробуйте если хотите)

Вещи не ломаются. Убрать отнятие "здоровья" у вещи


]]></description><link>https://celua.ru/topic/74/крафт-в-starsone</link><guid isPermaLink="true">https://celua.ru/topic/74/крафт-в-starsone</guid><dc:creator><![CDATA[MasterGH]]></dc:creator><pubDate>Invalid Date</pubDate></item><item><title><![CDATA[[The Escapists 2 + Unity + dnSpy] Спавн итемов]]></title><description><![CDATA[Спавн итемов удалось сделать через dnSpy
При клике на любой предмет на меню крафта он создается в инвентаре, а если там занято, то выкидывается на карту.
Я переписал метод клика на рецепт, вот он оригинальный
// Token: 0x060069FA RID: 27130 RVA: 0x00254874 File Offset: 0x00252A74
	private void OnRecipeSlotClicked(int recipeSlotID)
	{
		if (recipeSlotID == this.m_CurrentClickedRecipeID)
		{
			if (!T17RewiredStandaloneInputModule.IsCurrentActiveModuleUsingController())
			{
				this.m_CurrentClickedRecipeID = -1;
			}
			return;
		}
		if (!this.CheckHasItemsForRecipe(recipeSlotID))
		{
			this.m_CurrentClickedRecipeID = -1;
			return;
		}
		CraftManager.Recipe recipeByID = CraftManager.GetInstance().GetRecipeByID(recipeSlotID);
		if (recipeByID != null &amp;&amp; (!recipeByID.m_bHidden || recipeByID.m_bDiscovered))
		{
			this.FillSlots(recipeSlotID);
			this.m_CurrentClickedRecipeID = recipeSlotID;
			return;
		}
		this.m_CurrentClickedRecipeID = -1;
	}

На этот
private void OnRecipeSlotClicked(int recipeSlotID)
	{
		int itemDataID = CraftManager.GetInstance().GetCurrentRecipes()[recipeSlotID].m_Product.m_ItemDataID;
		Character character = base.CurrentGamePlayer.GetComponent();
		ItemManager.GetInstance().AssignItemRPC(character.m_NetView.ownerId, itemDataID, new ItemManager.ItemManagerEvent(this.OnStandingAnimItemSpawn), ref character.m_StandingStillEquipID, -1);
	}

	private void OnStandingAnimItemSpawn(Item item, int eventID)
	{
		if (base.CurrentGamePlayer.GetComponent().m_StandingStillEquipID == eventID)
		{
			Player player = base.CurrentGamePlayer;
			bool flag = false;
			if (player.GetEquippedItem() == null)
			{
				flag = player.SetEquippedItem(item, true, false, RPC_CallContexts.Master);
			}
			if (!flag)
			{
				item.DropItemInLevel(player, player.transform.position);
			}
		}
	}



Для спавна заменить Assembly-CSharp.dll из архива, сделав копию предварительно
Assembly-CSharp.rar
]]></description><link>https://celua.ru/topic/67/the-escapists-2-unity-dnspy-спавн-итемов</link><guid isPermaLink="true">https://celua.ru/topic/67/the-escapists-2-unity-dnspy-спавн-итемов</guid><dc:creator><![CDATA[MasterGH]]></dc:creator><pubDate>Invalid Date</pubDate></item><item><title><![CDATA[[dnSpy + Unity ] Пошаговая отладка]]></title><description><![CDATA[Эта статья позволит отлаживать игры на Unity в Runtime и при этом видеть декомпилированный код.
Это, конечно, не для всех игр на Unity. Пошаговая отладка в dnSpy позволяет ставить брейкпоинты на C# код во время работы игры, перемещаться по коду, просматривать значения переменных. Сэкономит кучу времени при поиске и отладке игрового кода,


Страница загрузки dnSpy


Скачиваем dnSpy и все архивы с mono.dll файлами
Unity-debugging-4.x-win32.zip
Unity-debugging-4.x-win64.zip
Unity-debugging-win32.zip
Unity-debugging-win64.zip


Смотрим свойства exe файла игры и определяем по нему версию Unity. Например, "Версия продукта 5.5.0.3120186" или версия "файла 5.50.39994" может указывать на версию Unity 5.5.


Определяем разрядность приложения через Process Explorer


Т.к. версия Unity 5.5 и приложение 32 разрядное, то открываем Unity-debugging-win32.zip ищем там версию Unity и заменяем mono.dll в директории игры


Запускаем игру и dnSpy x86 (игра 32 разрядная поэтому x86). Открываем файл "...\Managed\Assembly-CSharp.dll" Запускаем отладку нажав F5 или кнопку Play


Настраиваем соединение и жмем ок



Ставим брейкпоинты, смотрим перемененные, перемещаемся по коду, пишем свой код и так далее



После изменения кода, нужно перезаписать модуль предварительно сохранив его


О других способах подключения пошаговой отладки есть на английском руководство.


Как работать в пошаговой отладке
Работать можно почти также как и в Cheat Engine в пошаговой отладке или в среде разработки программ.
Начать стоит с обзора названия пространств имен, названия классов, методов и полей, Названия могут подсказать логическую связь с читом, который хотим сделать.
Стоит обратить внимание на такие названия как "IsPlayer, Player, Character, CharacterController, MainCharacter, Health, Inventory, Craft" и другие. Чтобы не искать вручную можно задействовать поиск сборкам. Поиск стандартного тега "Player" в виде в строки кода (в Unity выше версии 5.0) или свойства "IsPlayer" может помочь найти игрока или отличить от чужих.
Важно представлять иерархию игровых объектов, которую мы не видим в dnSpy. Программист работая в Unity видит это окошко много лет и эту иерархию всегда представляет смотря на скрипты в dnSpy

Скрипты наследники от MonoBehavior могут находиться на игровом объекте и могут работать как с ним так и с другими объектами. Получается такая штука, что игровой объект всегда имеет Transform компонент с полями позиций, углами и scale. Классы Transform и GameObject самые основные. Методами этих классов можно разместить объект в мире, создать или удалить его. В идеале удалив объект со сцены не должно быть никаких ошибок связанных с пустыми ссылками, потерей объекта. Также и клонировав объект, тоже не должно быть ошибок. Но не всегда так просто отспавнить игровой объект. Если это сделать методами UnityEngine, то другие классы ничего не будут знать о появлении игрового объекта. Нужно ставить брейкпоинт в функции Start или Awake в классе и трейсить по Shift+11 чтобы выйти на функцию разработчиков спавна этого GameObject. Функции Start или Awake (в классе наследника от MonoBehavior) срабатывают один раз при включении скрипта и инициализации. По ним можно выйти на строку кода, которая создает объекты в мире.
Отдельно стоит сказать про количество скриптов. Практически в любой игре, которая мне попадалась в dnSpy много скриптов или очень много. Иногда и не будет понятных названий у типов (из-за обфускации). В любом случае при пошаговой отладке можно найти требующиеся участки кода для создания чита и использовать их по другой логике.
Основные приемы
В Update можно обновлять параметры только своего игрока. Например, в Character классе сделать сравнения в Update по IsPlayer свойству (если оно там есть) и у тебя за каждый кадр рендеринга будет максимум характеристик.
В Update с классом Input можно считывать хоткеи.
В Start и Awake можно подгружать свои ассеты с внутриигровым user interface. Код скриптов перед загрузкой ассетов должен быть внедрен через dnSpy
Иерархию игровых объектов и инспектор, если очень нужно, то можно отрисовать в user interface. Обычно не требуется. (поищите по форуму в игрострое)
В заключении
Пока нет времени делать трейнер или таблицу на CE для включения опций в играх Unity. Для меня пока подходит способ через перезапись модуля в dnSpy вручную.
Вместо трейнера можно сделать программу патчер, который будет проверять версию игры и перезаписывать модуль с возможностью вернуть оригинальный модуль
]]></description><link>https://celua.ru/topic/66/dnspy-unity-пошаговая-отладка</link><guid isPermaLink="true">https://celua.ru/topic/66/dnspy-unity-пошаговая-отладка</guid><dc:creator><![CDATA[MasterGH]]></dc:creator><pubDate>Invalid Date</pubDate></item></channel></rss>