first commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class CameraFollow : MonoBehaviour
|
||||
{
|
||||
public Transform target;
|
||||
public Vector3 offset = new Vector3(0f, 8f, -7f);
|
||||
public float followSpeed = 8f;
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 desiredPosition = target.position + offset;
|
||||
transform.position = Vector3.Lerp(transform.position, desiredPosition, followSpeed * Time.deltaTime);
|
||||
transform.rotation = Quaternion.Euler(58f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ce9cf7cdc7900e2183e5ea50b33da7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class CraftingDemoController : MonoBehaviour
|
||||
{
|
||||
public Inventory inventory;
|
||||
public CraftingSystem craftingSystem;
|
||||
public GameObject craftingPanel;
|
||||
public Text hintText;
|
||||
public bool addStartingResources;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (addStartingResources && inventory != null && inventory.slots.Count == 0)
|
||||
{
|
||||
inventory.AddTestResources();
|
||||
}
|
||||
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.lastMessage = "Нажмите кнопку рецепта, чтобы создать предмет.";
|
||||
craftingSystem.Refresh();
|
||||
}
|
||||
|
||||
UpdateHint();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.R) && inventory != null)
|
||||
{
|
||||
inventory.AddTestResources();
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.lastMessage = "Инвентарь сброшен к тестовому набору ресурсов.";
|
||||
craftingSystem.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.C) && craftingPanel != null)
|
||||
{
|
||||
craftingPanel.SetActive(!craftingPanel.activeSelf);
|
||||
UpdateHint();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHint()
|
||||
{
|
||||
if (hintText != null)
|
||||
{
|
||||
hintText.text = "WASD - движение | E - подобрать | 1-9/0 - выбрать слот | F - использовать | R - тестовые ресурсы | C - UI";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e84b636784b7d2fc1a62fc0f6defb8fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public class CraftingSystem : MonoBehaviour
|
||||
{
|
||||
public Inventory playerInventory;
|
||||
public List<RecipeData> availableRecipes = new List<RecipeData>();
|
||||
[TextArea(1, 3)] public string lastMessage;
|
||||
|
||||
public event Action OnCraftingChanged;
|
||||
|
||||
public bool CanCraft(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || playerInventory == null || recipe.result == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recipe.unlockedByDefault || recipe.ingredients == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null || ingredient.amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!playerInventory.HasItem(ingredient.item, ingredient.amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return playerInventory.CanAddItem(recipe.result, recipe.resultAmount);
|
||||
}
|
||||
|
||||
public void Craft(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null)
|
||||
{
|
||||
lastMessage = "Рецепт не выбран.";
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerInventory == null)
|
||||
{
|
||||
lastMessage = "Инвентарь не подключен.";
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasIngredients(recipe))
|
||||
{
|
||||
lastMessage = "Не хватает ресурсов: " + GetMissingIngredientsText(recipe);
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipe.result == null || !playerInventory.CanAddItem(recipe.result, recipe.resultAmount))
|
||||
{
|
||||
lastMessage = "Недостаточно места в инвентаре для результата.";
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ingredients are removed only after confirming that the result can be added.
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
playerInventory.RemoveItem(ingredient.item, ingredient.amount);
|
||||
}
|
||||
|
||||
playerInventory.AddItem(recipe.result, recipe.resultAmount);
|
||||
lastMessage = "Создан предмет: " + recipe.GetResultText();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public string GetMissingIngredientsText(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || recipe.ingredients == null || playerInventory == null)
|
||||
{
|
||||
return "нет данных";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int current = playerInventory.GetItemAmount(ingredient.item);
|
||||
if (current >= ingredient.amount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
builder.Append(ingredient.item.itemName);
|
||||
builder.Append(" ");
|
||||
builder.Append(current);
|
||||
builder.Append("/");
|
||||
builder.Append(ingredient.amount);
|
||||
}
|
||||
|
||||
return builder.Length == 0 ? "место в инвентаре" : builder.ToString();
|
||||
}
|
||||
|
||||
public void SetRecipes(List<RecipeData> recipes)
|
||||
{
|
||||
availableRecipes = recipes ?? new List<RecipeData>();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
OnCraftingChanged?.Invoke();
|
||||
}
|
||||
|
||||
private bool HasIngredients(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || recipe.ingredients == null || playerInventory == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null || ingredient.amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!playerInventory.HasItem(ingredient.item, ingredient.amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d65badc258c79888b99baf10cba8731b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,381 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class CraftingUI : MonoBehaviour
|
||||
{
|
||||
public CraftingSystem craftingSystem;
|
||||
public Inventory inventory;
|
||||
public Transform recipeContainer;
|
||||
public GameObject recipeButtonPrefab;
|
||||
public Text messageText;
|
||||
public Text selectedRecipeText;
|
||||
public Font uiFont;
|
||||
|
||||
private readonly List<GameObject> recipeButtons = new List<GameObject>();
|
||||
private RecipeData selectedRecipe;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.OnCraftingChanged += RefreshRecipeButtons;
|
||||
}
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged += RefreshRecipeButtons;
|
||||
}
|
||||
|
||||
BuildRecipeList();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.OnCraftingChanged -= RefreshRecipeButtons;
|
||||
}
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged -= RefreshRecipeButtons;
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildRecipeList()
|
||||
{
|
||||
ClearRecipeButtons();
|
||||
|
||||
if (craftingSystem == null || recipeContainer == null || recipeButtonPrefab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigureRecipeContainer();
|
||||
|
||||
for (int i = 0; i < craftingSystem.availableRecipes.Count; i++)
|
||||
{
|
||||
RecipeData recipe = craftingSystem.availableRecipes[i];
|
||||
if (recipe == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GameObject buttonObject = Instantiate(recipeButtonPrefab, recipeContainer);
|
||||
buttonObject.SetActive(true);
|
||||
ConfigureRecipeButtonFrame(buttonObject, i);
|
||||
recipeButtons.Add(buttonObject);
|
||||
|
||||
Button button = buttonObject.GetComponent<Button>();
|
||||
if (button != null)
|
||||
{
|
||||
RecipeData capturedRecipe = recipe;
|
||||
button.onClick.AddListener(() => OnCraftButtonClicked(capturedRecipe));
|
||||
}
|
||||
}
|
||||
|
||||
RefreshRecipeButtons();
|
||||
if (selectedRecipe == null && craftingSystem.availableRecipes.Count > 0)
|
||||
{
|
||||
SelectRecipe(craftingSystem.availableRecipes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshRecipeButtons()
|
||||
{
|
||||
if (craftingSystem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recipeButtons.Count; i++)
|
||||
{
|
||||
GameObject buttonObject = recipeButtons[i];
|
||||
RecipeData recipe = i < craftingSystem.availableRecipes.Count ? craftingSystem.availableRecipes[i] : null;
|
||||
if (buttonObject == null || recipe == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool canCraft = craftingSystem.CanCraft(recipe);
|
||||
Button button = buttonObject.GetComponent<Button>();
|
||||
if (button != null)
|
||||
{
|
||||
button.interactable = canCraft;
|
||||
}
|
||||
|
||||
Image background = buttonObject.GetComponent<Image>();
|
||||
if (background != null)
|
||||
{
|
||||
background.color = canCraft
|
||||
? new Color(0.12f, 0.24f, 0.18f, 0.98f)
|
||||
: new Color(0.13f, 0.16f, 0.18f, 0.98f);
|
||||
}
|
||||
|
||||
Image stateStripe = buttonObject.transform.Find("StateStripe")?.GetComponent<Image>();
|
||||
if (stateStripe != null)
|
||||
{
|
||||
stateStripe.color = canCraft
|
||||
? new Color(0.25f, 0.75f, 0.38f, 1f)
|
||||
: new Color(0.44f, 0.48f, 0.52f, 1f);
|
||||
}
|
||||
|
||||
Text title = buttonObject.transform.Find("Title")?.GetComponent<Text>();
|
||||
if (title != null)
|
||||
{
|
||||
title.text = recipe.recipeName;
|
||||
}
|
||||
|
||||
Text result = buttonObject.transform.Find("Result")?.GetComponent<Text>();
|
||||
if (result != null)
|
||||
{
|
||||
result.text = recipe.GetResultText();
|
||||
}
|
||||
|
||||
Text ingredients = buttonObject.transform.Find("Ingredients")?.GetComponent<Text>();
|
||||
if (ingredients != null)
|
||||
{
|
||||
ingredients.text = recipe.GetIngredientsText();
|
||||
}
|
||||
|
||||
Text status = buttonObject.transform.Find("Status")?.GetComponent<Text>();
|
||||
if (status != null)
|
||||
{
|
||||
status.text = canCraft ? "ГОТОВО" : "НЕТ РЕС.";
|
||||
status.color = canCraft
|
||||
? new Color(0.76f, 0.96f, 0.72f)
|
||||
: new Color(0.94f, 0.72f, 0.62f);
|
||||
}
|
||||
|
||||
Text label = buttonObject.transform.Find("Label")?.GetComponent<Text>();
|
||||
if (label != null)
|
||||
{
|
||||
label.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (messageText != null)
|
||||
{
|
||||
messageText.text = craftingSystem.lastMessage;
|
||||
}
|
||||
|
||||
if (selectedRecipe != null)
|
||||
{
|
||||
SelectRecipe(selectedRecipe);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectRecipe(RecipeData recipe)
|
||||
{
|
||||
selectedRecipe = recipe;
|
||||
if (selectedRecipeText == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipe == null)
|
||||
{
|
||||
selectedRecipeText.text = "Рецепт не выбран";
|
||||
return;
|
||||
}
|
||||
|
||||
string missing = craftingSystem != null && !craftingSystem.CanCraft(recipe)
|
||||
? "\nНе хватает: " + craftingSystem.GetMissingIngredientsText(recipe)
|
||||
: "\nМожно создать сейчас.";
|
||||
|
||||
selectedRecipeText.text =
|
||||
recipe.recipeName + ": " + recipe.description + "\n" +
|
||||
"Ингредиенты: " + recipe.GetIngredientsText() + "\n" +
|
||||
"Результат: " + recipe.GetResultText() + missing;
|
||||
}
|
||||
|
||||
public void OnCraftButtonClicked(RecipeData recipe)
|
||||
{
|
||||
SelectRecipe(recipe);
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.Craft(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearRecipeButtons()
|
||||
{
|
||||
recipeButtons.Clear();
|
||||
|
||||
if (recipeContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = recipeContainer.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Destroy(recipeContainer.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureRecipeContainer()
|
||||
{
|
||||
RectTransform containerRect = recipeContainer as RectTransform;
|
||||
if (containerRect == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VerticalLayoutGroup layoutGroup = recipeContainer.GetComponent<VerticalLayoutGroup>();
|
||||
if (layoutGroup != null)
|
||||
{
|
||||
layoutGroup.enabled = false;
|
||||
}
|
||||
|
||||
containerRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
containerRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
containerRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
containerRect.anchoredPosition = new Vector2(0f, -8f);
|
||||
containerRect.sizeDelta = new Vector2(336f, 286f);
|
||||
|
||||
Image panelImage = GetComponent<Image>();
|
||||
if (panelImage != null)
|
||||
{
|
||||
panelImage.color = new Color(0.055f, 0.070f, 0.085f, 0.97f);
|
||||
}
|
||||
|
||||
Text title = transform.Find("Recipe Title")?.GetComponent<Text>();
|
||||
if (title != null)
|
||||
{
|
||||
SetRect(title.rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 160f), new Vector2(336f, 30f), new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
|
||||
Text debug = transform.Find("Recipe Debug")?.GetComponent<Text>();
|
||||
if (debug != null)
|
||||
{
|
||||
SetRect(debug.rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -162f), new Vector2(336f, 24f), new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureRecipeButtonFrame(GameObject buttonObject, int index)
|
||||
{
|
||||
RectTransform rect = buttonObject.GetComponent<RectTransform>();
|
||||
if (rect != null)
|
||||
{
|
||||
rect.anchorMin = new Vector2(0f, 1f);
|
||||
rect.anchorMax = new Vector2(0f, 1f);
|
||||
rect.pivot = new Vector2(0f, 1f);
|
||||
rect.anchoredPosition = new Vector2(0f, -index * 58f);
|
||||
rect.sizeDelta = new Vector2(336f, 52f);
|
||||
rect.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
Image background = buttonObject.GetComponent<Image>();
|
||||
if (background != null)
|
||||
{
|
||||
background.color = new Color(0.13f, 0.16f, 0.18f, 0.98f);
|
||||
}
|
||||
|
||||
Text oldLabel = buttonObject.transform.Find("Label")?.GetComponent<Text>();
|
||||
Font font = ResolveUIFont(oldLabel);
|
||||
if (oldLabel != null)
|
||||
{
|
||||
oldLabel.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
Image stateStripe = EnsureImageChild(buttonObject.transform, "StateStripe", new Color(0.44f, 0.48f, 0.52f, 1f));
|
||||
SetRect(stateStripe.rectTransform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(3f, 0f), new Vector2(6f, 44f), new Vector2(0.5f, 0.5f));
|
||||
|
||||
Text title = EnsureTextChild(buttonObject.transform, "Title", font, 13, FontStyle.Bold, TextAnchor.UpperLeft, Color.white);
|
||||
SetRect(title.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(110f, -10f), new Vector2(190f, 20f), new Vector2(0.5f, 0.5f));
|
||||
|
||||
Text result = EnsureTextChild(buttonObject.transform, "Result", font, 12, FontStyle.Bold, TextAnchor.UpperRight, new Color(0.78f, 0.92f, 1f));
|
||||
SetRect(result.rectTransform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-58f, -10f), new Vector2(100f, 20f), new Vector2(0.5f, 0.5f));
|
||||
|
||||
Text ingredients = EnsureTextChild(buttonObject.transform, "Ingredients", font, 9, FontStyle.Normal, TextAnchor.LowerLeft, new Color(0.82f, 0.86f, 0.88f));
|
||||
ingredients.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
ingredients.verticalOverflow = VerticalWrapMode.Truncate;
|
||||
SetRect(ingredients.rectTransform, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(130f, 12f), new Vector2(230f, 20f), new Vector2(0.5f, 0.5f));
|
||||
|
||||
Text status = EnsureTextChild(buttonObject.transform, "Status", font, 10, FontStyle.Bold, TextAnchor.LowerRight, new Color(0.94f, 0.72f, 0.62f));
|
||||
SetRect(status.rectTransform, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-46f, 12f), new Vector2(76f, 18f), new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
|
||||
private Text EnsureTextChild(Transform parent, string childName, Font font, int fontSize, FontStyle style, TextAnchor alignment, Color color)
|
||||
{
|
||||
Transform existing = parent.Find(childName);
|
||||
GameObject child = existing != null ? existing.gameObject : new GameObject(childName, typeof(RectTransform), typeof(Text));
|
||||
child.transform.SetParent(parent, false);
|
||||
|
||||
Text text = child.GetComponent<Text>();
|
||||
text.font = font != null ? font : ResolveUIFont(null);
|
||||
text.fontSize = fontSize;
|
||||
text.fontStyle = style;
|
||||
text.alignment = alignment;
|
||||
text.color = color;
|
||||
text.raycastTarget = false;
|
||||
text.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
text.verticalOverflow = VerticalWrapMode.Truncate;
|
||||
child.SetActive(true);
|
||||
return text;
|
||||
}
|
||||
|
||||
private Font ResolveUIFont(Text preferredText)
|
||||
{
|
||||
if (uiFont != null)
|
||||
{
|
||||
return uiFont;
|
||||
}
|
||||
|
||||
if (preferredText != null && preferredText.font != null)
|
||||
{
|
||||
uiFont = preferredText.font;
|
||||
return uiFont;
|
||||
}
|
||||
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
Text[] texts = canvas.GetComponentsInChildren<Text>(true);
|
||||
for (int i = 0; i < texts.Length; i++)
|
||||
{
|
||||
if (texts[i] != null && texts[i].font != null && texts[i].font.name.ToLowerInvariant().Contains("ubuntu"))
|
||||
{
|
||||
uiFont = texts[i].font;
|
||||
return uiFont;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < texts.Length; i++)
|
||||
{
|
||||
if (texts[i] != null && texts[i].font != null)
|
||||
{
|
||||
uiFont = texts[i].font;
|
||||
return uiFont;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||||
return uiFont;
|
||||
}
|
||||
|
||||
private Image EnsureImageChild(Transform parent, string childName, Color color)
|
||||
{
|
||||
Transform existing = parent.Find(childName);
|
||||
GameObject child = existing != null ? existing.gameObject : new GameObject(childName, typeof(RectTransform), typeof(Image));
|
||||
child.transform.SetParent(parent, false);
|
||||
|
||||
Image image = child.GetComponent<Image>();
|
||||
image.color = color;
|
||||
image.raycastTarget = false;
|
||||
child.SetActive(true);
|
||||
return image;
|
||||
}
|
||||
|
||||
private void SetRect(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 sizeDelta, Vector2 pivot)
|
||||
{
|
||||
rect.anchorMin = anchorMin;
|
||||
rect.anchorMax = anchorMax;
|
||||
rect.pivot = pivot;
|
||||
rect.anchoredPosition = anchoredPosition;
|
||||
rect.sizeDelta = sizeDelta;
|
||||
rect.localScale = Vector3.one;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a203fb84ce0011b84b247ff5cd148925
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,117 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class HotbarUI : MonoBehaviour
|
||||
{
|
||||
public Inventory inventory;
|
||||
public ItemUseSystem itemUseSystem;
|
||||
public Transform slotContainer;
|
||||
public GameObject slotPrefab;
|
||||
public int slotCount = 10;
|
||||
|
||||
private readonly List<GameObject> views = new List<GameObject>();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged += RefreshUI;
|
||||
}
|
||||
|
||||
if (itemUseSystem != null)
|
||||
{
|
||||
itemUseSystem.OnSelectionChanged += RefreshUI;
|
||||
}
|
||||
|
||||
BuildSlots();
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged -= RefreshUI;
|
||||
}
|
||||
|
||||
if (itemUseSystem != null)
|
||||
{
|
||||
itemUseSystem.OnSelectionChanged -= RefreshUI;
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildSlots()
|
||||
{
|
||||
ClearSlots();
|
||||
if (slotContainer == null || slotPrefab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < slotCount; i++)
|
||||
{
|
||||
GameObject view = Instantiate(slotPrefab, slotContainer);
|
||||
view.SetActive(true);
|
||||
int capturedIndex = i;
|
||||
|
||||
Button button = view.GetComponent<Button>();
|
||||
if (button != null && itemUseSystem != null)
|
||||
{
|
||||
button.onClick.AddListener(() => itemUseSystem.SelectSlot(capturedIndex));
|
||||
}
|
||||
|
||||
views.Add(view);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshUI()
|
||||
{
|
||||
for (int i = 0; i < views.Count; i++)
|
||||
{
|
||||
GameObject view = views[i];
|
||||
InventorySlot slot = inventory != null ? inventory.GetSlot(i) : null;
|
||||
|
||||
Text label = view.transform.Find("Label")?.GetComponent<Text>();
|
||||
if (label != null)
|
||||
{
|
||||
label.text = slot != null ? slot.item.itemName + "\nx" + slot.amount : "Пусто";
|
||||
}
|
||||
|
||||
Text number = view.transform.Find("Number")?.GetComponent<Text>();
|
||||
if (number != null)
|
||||
{
|
||||
number.text = i == 9 ? "0" : (i + 1).ToString();
|
||||
}
|
||||
|
||||
Image marker = view.transform.Find("ColorMarker")?.GetComponent<Image>();
|
||||
if (marker != null)
|
||||
{
|
||||
marker.color = slot != null ? slot.item.itemColor : new Color(0.18f, 0.20f, 0.22f, 1f);
|
||||
}
|
||||
|
||||
Image background = view.GetComponent<Image>();
|
||||
if (background != null)
|
||||
{
|
||||
bool selected = itemUseSystem != null && itemUseSystem.selectedSlotIndex == i;
|
||||
background.color = selected
|
||||
? new Color(0.82f, 0.66f, 0.30f, 0.96f)
|
||||
: new Color(0.10f, 0.12f, 0.15f, 0.92f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSlots()
|
||||
{
|
||||
views.Clear();
|
||||
if (slotContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = slotContainer.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Destroy(slotContainer.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2b8484cf82d01ee7afde9b971f40251
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public class Inventory : MonoBehaviour
|
||||
{
|
||||
public List<InventorySlot> slots = new List<InventorySlot>();
|
||||
public int maxSlots = 32;
|
||||
public List<InventorySlot> testResources = new List<InventorySlot>();
|
||||
|
||||
public event Action OnInventoryChanged;
|
||||
|
||||
public InventorySlot GetSlot(int index)
|
||||
{
|
||||
if (index < 0 || index >= slots.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
InventorySlot slot = slots[index];
|
||||
return slot == null || slot.IsEmpty() ? null : slot;
|
||||
}
|
||||
|
||||
public bool HasItem(ItemData item, int amount)
|
||||
{
|
||||
if (item == null || amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetItemAmount(item) >= amount;
|
||||
}
|
||||
|
||||
public int GetItemAmount(ItemData item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
{
|
||||
InventorySlot slot = slots[i];
|
||||
if (slot != null && slot.item == item && slot.amount > 0)
|
||||
{
|
||||
total += slot.amount;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public bool AddItem(ItemData item, int amount)
|
||||
{
|
||||
if (!CanAddItem(item, amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AddItemInternal(item, amount);
|
||||
RaiseChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveItem(ItemData item, int amount)
|
||||
{
|
||||
if (item == null || amount <= 0 || !HasItem(item, amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int remaining = amount;
|
||||
for (int i = slots.Count - 1; i >= 0 && remaining > 0; i--)
|
||||
{
|
||||
InventorySlot slot = slots[i];
|
||||
if (slot == null || slot.item != item || slot.amount <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int removed = Mathf.Min(slot.amount, remaining);
|
||||
slot.amount -= removed;
|
||||
remaining -= removed;
|
||||
|
||||
if (slot.IsEmpty())
|
||||
{
|
||||
slots.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
RaiseChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveFromSlot(int slotIndex, int amount)
|
||||
{
|
||||
InventorySlot slot = GetSlot(slotIndex);
|
||||
if (slot == null || amount <= 0 || slot.amount < amount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
slot.amount -= amount;
|
||||
if (slot.IsEmpty())
|
||||
{
|
||||
slots.RemoveAt(slotIndex);
|
||||
}
|
||||
|
||||
RaiseChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
slots.Clear();
|
||||
RaiseChanged();
|
||||
}
|
||||
|
||||
public void AddTestResources()
|
||||
{
|
||||
slots.Clear();
|
||||
for (int i = 0; i < testResources.Count; i++)
|
||||
{
|
||||
InventorySlot resource = testResources[i];
|
||||
if (resource != null && resource.item != null && resource.amount > 0)
|
||||
{
|
||||
AddItemInternal(resource.item, resource.amount);
|
||||
}
|
||||
}
|
||||
|
||||
RaiseChanged();
|
||||
}
|
||||
|
||||
public string GetInventoryDebugText()
|
||||
{
|
||||
if (slots.Count == 0)
|
||||
{
|
||||
return "Инвентарь пуст";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
{
|
||||
InventorySlot slot = slots[i];
|
||||
if (slot == null || slot.IsEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AppendLine(slot.GetDisplayText());
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
// Used by CraftingSystem to prevent losing ingredients when the result has no room.
|
||||
public bool CanAddItem(ItemData item, int amount)
|
||||
{
|
||||
if (item == null || amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int remaining = amount;
|
||||
int maxStack = Mathf.Max(1, item.maxStack);
|
||||
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
{
|
||||
InventorySlot slot = slots[i];
|
||||
if (slot != null && slot.item == item && slot.amount > 0 && slot.amount < maxStack)
|
||||
{
|
||||
remaining -= maxStack - slot.amount;
|
||||
if (remaining <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int freeSlots = Mathf.Max(0, maxSlots - slots.Count);
|
||||
int requiredSlots = Mathf.CeilToInt(remaining / (float)maxStack);
|
||||
return requiredSlots <= freeSlots;
|
||||
}
|
||||
|
||||
private void AddItemInternal(ItemData item, int amount)
|
||||
{
|
||||
int remaining = amount;
|
||||
int maxStack = Mathf.Max(1, item.maxStack);
|
||||
|
||||
for (int i = 0; i < slots.Count && remaining > 0; i++)
|
||||
{
|
||||
InventorySlot slot = slots[i];
|
||||
if (slot == null || slot.item != item || slot.amount >= maxStack)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int added = Mathf.Min(maxStack - slot.amount, remaining);
|
||||
slot.amount += added;
|
||||
remaining -= added;
|
||||
}
|
||||
|
||||
while (remaining > 0 && slots.Count < maxSlots)
|
||||
{
|
||||
int added = Mathf.Min(maxStack, remaining);
|
||||
slots.Add(new InventorySlot(item, added));
|
||||
remaining -= added;
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseChanged()
|
||||
{
|
||||
OnInventoryChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c51713eb2f7200edb1670500e651059
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using UnityEngine;
|
||||
|
||||
[System.Serializable]
|
||||
public class InventorySlot
|
||||
{
|
||||
public ItemData item;
|
||||
[Min(0)] public int amount;
|
||||
|
||||
public InventorySlot(ItemData item, int amount)
|
||||
{
|
||||
this.item = item;
|
||||
this.amount = Mathf.Max(0, amount);
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return item == null || amount <= 0;
|
||||
}
|
||||
|
||||
public string GetDisplayText()
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
return "Пусто";
|
||||
}
|
||||
|
||||
return item.itemName + " x" + amount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b0c3eec8650bed31a58bd30ca562fc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class InventoryUI : MonoBehaviour
|
||||
{
|
||||
public Inventory inventory;
|
||||
public Transform slotContainer;
|
||||
public GameObject slotPrefab;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged += RefreshUI;
|
||||
}
|
||||
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged -= RefreshUI;
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshUI()
|
||||
{
|
||||
if (slotContainer == null || slotPrefab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearSlots();
|
||||
|
||||
if (inventory == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < inventory.slots.Count; i++)
|
||||
{
|
||||
InventorySlot slot = inventory.slots[i];
|
||||
if (slot == null || slot.IsEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GameObject view = Instantiate(slotPrefab, slotContainer);
|
||||
view.SetActive(true);
|
||||
|
||||
Text label = view.GetComponentInChildren<Text>(true);
|
||||
if (label != null)
|
||||
{
|
||||
label.text = slot.GetDisplayText();
|
||||
}
|
||||
|
||||
Image colorMarker = view.transform.Find("ColorMarker")?.GetComponent<Image>();
|
||||
if (colorMarker != null && slot.item != null)
|
||||
{
|
||||
colorMarker.color = slot.item.itemColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSlots()
|
||||
{
|
||||
if (slotContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = slotContainer.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Destroy(slotContainer.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8094cc4f3c11692fb46e46ff90ae99f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(fileName = "NewItem", menuName = "Crafting/ItemData")]
|
||||
public class ItemData : ScriptableObject
|
||||
{
|
||||
[Header("Identity")]
|
||||
public string itemId;
|
||||
public string itemName;
|
||||
[TextArea(2, 4)] public string description;
|
||||
|
||||
[Header("Presentation")]
|
||||
public Sprite icon;
|
||||
public Color itemColor = Color.white;
|
||||
|
||||
[Header("Stacking")]
|
||||
[Min(1)] public int maxStack = 64;
|
||||
public bool isResource;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ef36b19b094710baaaabf5321e6d693
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class ItemUseSystem : MonoBehaviour
|
||||
{
|
||||
public Inventory inventory;
|
||||
public CraftingSystem craftingSystem;
|
||||
public PlayerMovement playerMovement;
|
||||
public Transform playerTransform;
|
||||
public Material torchLightMaterial;
|
||||
public Text statusText;
|
||||
public int selectedSlotIndex;
|
||||
public int selectableSlotCount = 10;
|
||||
|
||||
public event Action OnSelectionChanged;
|
||||
|
||||
public void SelectSlot(int slotIndex)
|
||||
{
|
||||
selectedSlotIndex = Mathf.Clamp(slotIndex, 0, Mathf.Max(0, selectableSlotCount - 1));
|
||||
OnSelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
for (int i = 0; i < selectableSlotCount && i < 10; i++)
|
||||
{
|
||||
KeyCode keyCode = i == 9 ? KeyCode.Alpha0 : (KeyCode)((int)KeyCode.Alpha1 + i);
|
||||
if (Input.GetKeyDown(keyCode))
|
||||
{
|
||||
SelectSlot(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.F))
|
||||
{
|
||||
UseSelectedItem();
|
||||
}
|
||||
}
|
||||
|
||||
public void UseSelectedItem()
|
||||
{
|
||||
if (inventory == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InventorySlot slot = inventory.GetSlot(selectedSlotIndex);
|
||||
if (slot == null || slot.item == null)
|
||||
{
|
||||
SetMessage("В выбранном слоте нет предмета.");
|
||||
return;
|
||||
}
|
||||
|
||||
string itemId = slot.item.itemId;
|
||||
if (itemId == "bandage")
|
||||
{
|
||||
inventory.RemoveFromSlot(selectedSlotIndex, 1);
|
||||
SetMessage("Использован бинт: здоровье восстановлено.");
|
||||
}
|
||||
else if (itemId == "torch")
|
||||
{
|
||||
inventory.RemoveFromSlot(selectedSlotIndex, 1);
|
||||
PlaceTorch();
|
||||
SetMessage("Факел установлен рядом с игроком.");
|
||||
}
|
||||
else if (itemId == "magic_amulet")
|
||||
{
|
||||
if (playerMovement != null)
|
||||
{
|
||||
playerMovement.ApplySpeedBuff(1.6f, 12f);
|
||||
}
|
||||
|
||||
SetMessage("Амулет активирован: скорость увеличена на 12 секунд.");
|
||||
}
|
||||
else if (itemId == "axe")
|
||||
{
|
||||
SetMessage("Топор готов к использованию. Им можно рубить древесину в расширенной версии.");
|
||||
}
|
||||
else if (itemId == "pickaxe")
|
||||
{
|
||||
SetMessage("Кирка готова к использованию. Им можно добывать камень и руду в расширенной версии.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMessage("Этот предмет нельзя использовать напрямую.");
|
||||
}
|
||||
|
||||
OnSelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void PlaceTorch()
|
||||
{
|
||||
if (playerTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject torch = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||
torch.name = "Placed Torch";
|
||||
torch.transform.position = playerTransform.position + playerTransform.forward * 0.9f + Vector3.up * 0.45f;
|
||||
torch.transform.localScale = new Vector3(0.12f, 0.45f, 0.12f);
|
||||
if (torchLightMaterial != null)
|
||||
{
|
||||
torch.GetComponent<Renderer>().material = torchLightMaterial;
|
||||
}
|
||||
|
||||
GameObject lightObject = new GameObject("Torch Light");
|
||||
lightObject.transform.SetParent(torch.transform, false);
|
||||
lightObject.transform.localPosition = new Vector3(0f, 0.75f, 0f);
|
||||
Light light = lightObject.AddComponent<Light>();
|
||||
light.type = LightType.Point;
|
||||
light.range = 5f;
|
||||
light.intensity = 2.2f;
|
||||
light.color = new Color(1f, 0.65f, 0.28f);
|
||||
}
|
||||
|
||||
private void SetMessage(string message)
|
||||
{
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.lastMessage = message;
|
||||
craftingSystem.Refresh();
|
||||
}
|
||||
|
||||
if (statusText != null)
|
||||
{
|
||||
statusText.text = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d737d15fbf341fd9582d9f0fa3366b7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class PlayerInteractor : MonoBehaviour
|
||||
{
|
||||
public Inventory inventory;
|
||||
public CraftingSystem craftingSystem;
|
||||
public float interactRadius = 1.8f;
|
||||
public Text interactionText;
|
||||
|
||||
private ResourcePickup nearestPickup;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
FindNearestPickup();
|
||||
|
||||
if (interactionText != null)
|
||||
{
|
||||
interactionText.text = nearestPickup != null
|
||||
? "E - подобрать: " + nearestPickup.GetPickupText()
|
||||
: "Подойдите к ресурсу и нажмите E";
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.E) && nearestPickup != null)
|
||||
{
|
||||
if (nearestPickup.TryCollect(inventory))
|
||||
{
|
||||
SetMessage("Подобрано: " + nearestPickup.GetPickupText());
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMessage("Инвентарь заполнен.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FindNearestPickup()
|
||||
{
|
||||
nearestPickup = null;
|
||||
Collider[] hits = Physics.OverlapSphere(transform.position, interactRadius);
|
||||
float bestDistance = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < hits.Length; i++)
|
||||
{
|
||||
ResourcePickup pickup = hits[i].GetComponentInParent<ResourcePickup>();
|
||||
if (pickup == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(transform.position, pickup.transform.position);
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
nearestPickup = pickup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetMessage(string message)
|
||||
{
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.lastMessage = message;
|
||||
craftingSystem.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a226e29ff6807deea808680d3168d748
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class PlayerMovement : MonoBehaviour
|
||||
{
|
||||
public float moveSpeed = 5f;
|
||||
public float rotationSpeed = 12f;
|
||||
|
||||
private CharacterController characterController;
|
||||
private float speedMultiplier = 1f;
|
||||
private float buffTimer;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
characterController = GetComponent<CharacterController>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
|
||||
if (input.sqrMagnitude > 1f)
|
||||
{
|
||||
input.Normalize();
|
||||
}
|
||||
|
||||
Vector3 move = input * moveSpeed * speedMultiplier;
|
||||
move.y = Physics.gravity.y * 0.2f;
|
||||
|
||||
if (characterController != null)
|
||||
{
|
||||
characterController.Move(move * Time.deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position += move * Time.deltaTime;
|
||||
}
|
||||
|
||||
if (input.sqrMagnitude > 0.01f)
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.LookRotation(input, Vector3.up);
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (buffTimer > 0f)
|
||||
{
|
||||
buffTimer -= Time.deltaTime;
|
||||
if (buffTimer <= 0f)
|
||||
{
|
||||
speedMultiplier = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplySpeedBuff(float multiplier, float duration)
|
||||
{
|
||||
speedMultiplier = Mathf.Max(1f, multiplier);
|
||||
buffTimer = Mathf.Max(0f, duration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48b98ce024e7a0859972c6cb9936944d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
[System.Serializable]
|
||||
public class Ingredient
|
||||
{
|
||||
public ItemData item;
|
||||
[Min(1)] public int amount = 1;
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "NewRecipe", menuName = "Crafting/RecipeData")]
|
||||
public class RecipeData : ScriptableObject
|
||||
{
|
||||
public string recipeId;
|
||||
public string recipeName;
|
||||
[TextArea(2, 4)] public string description;
|
||||
public Ingredient[] ingredients;
|
||||
public ItemData result;
|
||||
[Min(1)] public int resultAmount = 1;
|
||||
public bool unlockedByDefault = true;
|
||||
|
||||
public string GetIngredientsText()
|
||||
{
|
||||
if (ingredients == null || ingredients.Length == 0)
|
||||
{
|
||||
return "Без ингредиентов";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
builder.Append(ingredient.item.itemName);
|
||||
builder.Append(" x");
|
||||
builder.Append(ingredient.amount);
|
||||
}
|
||||
|
||||
return builder.Length == 0 ? "Без ингредиентов" : builder.ToString();
|
||||
}
|
||||
|
||||
public string GetResultText()
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return "Нет результата";
|
||||
}
|
||||
|
||||
return result.itemName + " x" + resultAmount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6df24091ce261531692311bf89ece59a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ResourcePickup : MonoBehaviour
|
||||
{
|
||||
public ItemData item;
|
||||
public int amount = 1;
|
||||
public float respawnSeconds = 8f;
|
||||
|
||||
private Renderer[] renderers;
|
||||
private Collider[] colliders;
|
||||
private bool available = true;
|
||||
private float respawnTimer;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
renderers = GetComponentsInChildren<Renderer>();
|
||||
colliders = GetComponentsInChildren<Collider>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
transform.Rotate(0f, 45f * Time.deltaTime, 0f, Space.World);
|
||||
|
||||
if (available)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
respawnTimer -= Time.deltaTime;
|
||||
if (respawnTimer <= 0f)
|
||||
{
|
||||
SetAvailable(true);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryCollect(Inventory inventory)
|
||||
{
|
||||
if (!available || inventory == null || item == null || amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!inventory.AddItem(item, amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetAvailable(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetPickupText()
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return "Неизвестный ресурс";
|
||||
}
|
||||
|
||||
return item.itemName + " x" + amount;
|
||||
}
|
||||
|
||||
private void SetAvailable(bool value)
|
||||
{
|
||||
available = value;
|
||||
respawnTimer = value ? 0f : respawnSeconds;
|
||||
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
{
|
||||
renderers[i].enabled = value;
|
||||
}
|
||||
|
||||
for (int i = 0; i < colliders.Length; i++)
|
||||
{
|
||||
colliders[i].enabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f26d71e91c354e59b8afd6844bd07a7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user