Unity3D仿星露谷物语开发43之农作物生长

news2025/5/20 8:56:00

1、目标

把防风草种子种在地里,并展示植物种子,防风草种子将随着时间变化而生长成植株。

2、创建Crop.cs脚本

在Assets -> Scripts下创建新的目录命名为Crop,在其下创建新的脚本命名为Crop.cs。

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Crop : MonoBehaviour
{
    [HideInInspector]
    public Vector2Int cropGridPosition;
}

3、创建CropDetails.cs脚本

在Assets -> Scripts -> Crop下创建CropDetails.cs脚本。

代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class CropDetails 
{
    [ItemCodeDescription]
    public int seedItemCode; // this is the item code for the corresponding seed
    public int[] growthDays; // days growth for each stage
    public int totalGrowthDays; // total growth days
    public GameObject[] growthPrefab; // prefab to use when instantiating growth stages
    public Sprite[] growthSprite; // growth sprite
    public Season[] seasons; // growth seasons
    public Sprite harvestedSprite; // sprite used once harvested

    [ItemCodeDescription]
    public int harvestedTransformItemCode; // if the item transform into another item when harvested this item code will be populated
    public bool hideCropBeforeHarvestedAnimation; // if the crop should be disabled before the harvested animation
    public bool disableCropCollidersBeforeHarvestedAnimation; // if colliders on crop should be disabled to avoid the harvested animation effecting any other game objects
    public bool isHarvestedAnimation; // true if harvested animation to be played on final growth stage prefab
    public bool isHarvestActionEffect = false; // flag to determine whether there is a harvest action effect
    public bool spawnCropProducedAtPlayerPosition;
    public HarvestActionEffect harvestActionEffect; // the harvest action effect for the crop

    [ItemCodeDescription]
    public int[] harvestToolItemCode; // array of item codes for the tools that can harvest or 0 array elements if no tool required
    public int[] requiredHarvestActions; // number of harvest actions required for corresponding tool in harvest tool item code array

    [ItemCodeDescription]
    public int[] cropProducedItemCode; // array of item codes produced for the harvested crop
    public int[] cropProducedMinQuantity; // array of minimum quantities produced for the harvested crop
    public int[] cropProducedMaxQuantity; // if max quantity is > min quantity then a random number of crops between min and max are produced
    public int daysToRegrow; // days to regrow next crop or -1 if a single crop

    /// <summary>
    /// returns true if the tool item code can be used to harvest this crop, else returns false
    /// </summary>
    /// <param name="toolItemCode"></param>
    /// <returns></returns>
    public bool CanUseToolToHarvestCrop(int toolItemCode)
    {
        if(RequiredHarvestActionsForTool(toolItemCode) == -1)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    /// <summary>
    /// returns -1 if the tool can't be used to harvest this crop, else returns thhe number of harvest actions required by this tool
    /// </summary>
    /// <param name="toolItemCode"></param>
    /// <returns></returns>
    public int RequiredHarvestActionsForTool(int toolItemCode)
    {
        for(int i = 0; i < harvestToolItemCode.Length; i++)
        {
            if (harvestToolItemCode[i] == toolItemCode)
            {
                return requiredHarvestActions[i];
            }
        }
        return -1;
    }
    
}

4、创建SO_CropDetailsList.cs脚本

在Assets -> Scripts -> Crop下创建SO_CropDetailsList.cs脚本。

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName ="CropDetailsList", menuName ="Scriptable Objects/Crop/Crop Details List")]
public class SO_CropDetailList : ScriptableObject
{
    [SerializeField]
    public List<CropDetails> cropDetails;

    public CropDetails GetCropDetails(int seedItemCode)
    {
        return cropDetails.Find(x => x.seedItemCode == seedItemCode);
    }
}

5、填充防风草作物的数据

(1)制作Crop预制体

第1步,在Hierarchy -> PersistentScene下创建新的物体命名为CropStandard。

第2步,给该物体添加Box Collider 2D组件,并设置参数如下:

第3步,添加Crop组件和Item Nudge组件。

第4步,在CropStandard下创建子物体命名为CropSprite。

第5步,给CropSprite添加Sprite Renderer组件,并且设置参数如下:

如果Sorting Layer未设置未Instances,将导致seed无法正确显示(因为优先级不够)。

第6步,将Hierarchy下的CropStandard拖到Assets -> Prefabs -> Crop目录下,并且删除Hierarchy下的CropStandard。

(2)创建Crop的Scriptable资源

第1步,在Assets -> Scriptable Object Assets 下创建新的目录命名为Crop。

第2步,右击Crop目录,选择Scriptable Objects -> Crop -> Crop Details List,命名为so_CropDetailsList。

第3步,配置so_CropDetailsList的信息如下:

6、防风草生长

(1)优化Tags.cs脚本

添加一个变量:

public const string CropsParentTransform = "CropsParentTransform";

(2)添加Crops对象

首先,加载Scene1_Farm场景

然后,在Hierarchy -> Scene1_Farm下创建新的物体命名为Crops

接着,在Inspector界面下创建新的Tag:CropsParentTransform,并且指定Crops的Tag为新Tag。

最后,Unload Scene1_Farm场景。

对Scene2_Field场景进行同样的操作

(3)优化EventHandler.cs脚本

添加新的事件如下:

// Remove selected item from inventory
public static event Action RemoveSelectedItemFromInventoryEvent;

public static void CallRemoveSelectedItemFromInventoryEvent()
{
    if(RemoveSelectedItemFromInventoryEvent != null)
    {
        RemoveSelectedItemFromInventoryEvent();
    }
}

(4)优化GridPropertiesManager.cs脚本

添加变量1:

private Transform cropParentTransform;

修改AfterSceneLoaded函数,添加如下代码:

if(GameObject.FindGameObjectWithTag(Tags.CropsParentTransform) != null)
{
    cropParentTransform = GameObject.FindGameObjectWithTag(Tags.CropsParentTransform).transform;
}
else
{
    cropParentTransform = null;
}

添加变量2:

[SerializeField] private SO_CropDetailList so_CropDetailList = null;

修改ClearDisplayGridPropertyDetails函数,添加如下代码:

private void ClearDisplayAllPlantedCrops()
{
    // Destory all crops in scene
    Crop[] cropArray;
    cropArray = FindObjectsOfType<Crop>();   

    foreach(Crop crop in cropArray)
    {
        Destroy(crop.gameObject);
    }

}

private void ClearDisplayGridPropertyDetails()
{
    ClearDisplayGroundDecorations();

    ClearDisplayAllPlantedCrops();
}

修改DisplayGridPropertyDetails函数,代码如下:

private void DisplayGridPropertyDetails()
{
    // Loop throught all grid items
    foreach(KeyValuePair<string, GridPropertyDetails> item in gridPropertyDictionary)
    {
        GridPropertyDetails gridPropertyDetails = item.Value;

        DisplayDugGround(gridPropertyDetails);

        DisplayWateredGround(gridPropertyDetails);

        DisplayPlantedCrop(gridPropertyDetails);
    }
}

public void DisplayPlantedCrop(GridPropertyDetails gridPropertyDetails)
{
    if(gridPropertyDetails.seedItemCode > -1)
    {
        // get crop details
        CropDetails cropDetails = so_CropDetailList.GetCropDetails(gridPropertyDetails.seedItemCode);

        // prefab to use
        GameObject cropPrefab;

        // instantiate crop prefab at grid location
        int growthStages = cropDetails.growthDays.Length;

        int currentGrowthStage = 0;
        int daysCounter = cropDetails.totalGrowthDays;

        // 找出目前所处的成长阶段
        for(int i = growthStages - 1; i >= 0; i--)
        {
            if(gridPropertyDetails.growthDays >= daysCounter)
            {
                currentGrowthStage = i;
                break;
            }

            daysCounter = daysCounter - cropDetails.growthDays[i];
        }

        cropPrefab = cropDetails.growthPrefab[currentGrowthStage];

        Sprite growthSprite = cropDetails.growthSprite[currentGrowthStage];

        Vector3 worldPosition = groundDecoration2.CellToWorld(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY, 0));

        worldPosition = new Vector3(worldPosition.x + Settings.gridCellSize / 2, worldPosition.y, worldPosition.z);

        GameObject cropInstance = Instantiate(cropPrefab, worldPosition, Quaternion.identity);

        cropInstance.GetComponentInChildren<SpriteRenderer>().sprite = growthSprite;
        cropInstance.transform.SetParent(cropParentTransform);
        cropInstance.GetComponent<Crop>().cropGridPosition = new Vector2Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY);
    
    }
}

修改AdvanceDay函数,代码如下:

private void AdvanceDay(int gameYear, Season gameSeason, int gameDay, string gameDayyOfWeek, int gameHour, int gameMinute, int gameSecond)
{
    // Clear Display All Grid Property Details
    ClearDisplayGridPropertyDetails();

    // loop through all scenes - by looping through all gridproperty in the array
    foreach(SO_GridProperties so_GridProperties in so_gridPropertiesArray)
    {
        // Get gridpropertydetails dictionary for scene
        if(GameObjectSave.sceneData.TryGetValue(so_GridProperties.sceneName.ToString(), out SceneSave sceneSave))
        {
            if(sceneSave.gridPropertyDetailsDictionary != null)
            {
                for(int i = sceneSave.gridPropertyDetailsDictionary.Count - 1; i >= 0; i--)
                {
                    KeyValuePair<string, GridPropertyDetails> item = sceneSave.gridPropertyDetailsDictionary.ElementAt(i);

                    GridPropertyDetails gridPropertyDetails = item.Value;

                    #region Update all grid properties to reflect the advance in the day

                    // if a crop is planted
                    if(gridPropertyDetails.growthDays > -1)
                    {
                        gridPropertyDetails.growthDays += 1;
                    }

                    // if ground is watered, then clear water
                    if(gridPropertyDetails.daysSinceWatered > -1)
                    {
                        gridPropertyDetails.daysSinceWatered = -1;
                    }

                    // Set gridpropertydetails
                    SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails, sceneSave.gridPropertyDetailsDictionary);

                    #endregion Update all grid properties to reflect the advance in the day
                }
            }
        }
    }

    // Display grid property details to reflect changed values
    DisplayGridPropertyDetails();
}

(5)优化Player.cs脚本

优化ProcessPlayerClickInput函数,代码如下:

修改ProcessPlayerClickInputSeed函数如下:

private void ProcessPlayerClickInputSeed(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails)
{
    if(itemDetails.canBeDropped && gridCursor.CursorPositionIsValid && gridPropertyDetails.daysSinceDug > -1 && gridPropertyDetails.seedItemCode == -1)
    {
        PlantSeedAtCursor(gridPropertyDetails, itemDetails);
    }
    else if (itemDetails.canBeDropped && gridCursor.CursorPositionIsValid)
    {
        EventHandler.CallDropSelectedItemEvent();
    }
}

添加PlantSeedAtCursor函数如下:

 private void PlantSeedAtCursor(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails)
 {
     // update grid properties with seed details
     gridPropertyDetails.seedItemCode = itemDetails.itemCode;
     gridPropertyDetails.growthDays = 0;

     // Display planted crop at grid property details
     GridPropertiesManager.Instance.DisplayPlantedCrop(gridPropertyDetails);

     // Remove item from inventory
     EventHandler.CallRemoveSelectedItemFromInventoryEvent();
 }

(6)优化UIInventorySlot.cs脚本

修改OnEnable代码,

 private void OnEnable()
 {
     EventHandler.AfterSceneLoadEvent += SceneLoaded;
     EventHandler.RemoveSelectedItemFromInventoryEvent += RemoveSelectedItemFromInventory;
     EventHandler.DropSelectedItemEvent += DropSelectedItemAtMousePosition;
 }

修改OnDisable代码:

private void OnDisable()
{
    EventHandler.AfterSceneLoadEvent -= SceneLoaded;
    EventHandler.RemoveSelectedItemFromInventoryEvent -= RemoveSelectedItemFromInventory;
    EventHandler.DropSelectedItemEvent -= DropSelectedItemAtMousePosition;
}

添加RemoveSelectedItemFromInventory函数代码:

 private void RemoveSelectedItemFromInventory()
 {
     if(itemDetails != null && isSelected)
     {
         int itemCode = itemDetails.itemCode;

         // Remove item from players inventory
         InventoryManager.Instance.RemoveItem(InventoryLocation.player, itemCode);

         // If no more of iitem then clear selected
         if(InventoryManager.Instance.FindItemInInventory(InventoryLocation.player, itemCode) == -1)
         {
             ClearSelectedItem();
         }
     }
 }

(7)补充配置信息

配置GridPropertiesManager的So_Crop Detail List信息如下:

7、运行游戏

先用Hoe挖一块地

然后洒下Parsnip的种子

按下G键可以加速时间,此时就可以看到防风草生长的过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0到1上手Kafka:开启分布式消息处理之旅

目录 一、Kafka 是什么 二、Kafka 的基础概念 2.1 核心术语解读 2.2 工作模式剖析 三、Kafka 的应用场景 四、Kafka 与其他消息队列的比较 五、Kafka 的安装与配置 5.1 环境准备 5.2 安装步骤 5.3 常见问题及解决 六、Kafka 的基本操作 6.1 命令行工具使用 6.1.1 …

Python爬虫如何应对网站的反爬加密策略?

在当今的互联网环境中&#xff0c;网络爬虫已经成为数据采集的重要工具之一。然而&#xff0c;随着网站安全意识的不断提高&#xff0c;反爬虫技术也越来越复杂&#xff0c;尤其是数据加密策略的广泛应用&#xff0c;给爬虫开发者带来了巨大的挑战。本文将详细介绍Python爬虫如…

第一次经历项目上线

这几天没写csdn&#xff0c;因为忙着项目上线的问题&#xff0c;我这阶段改了非常多的前端bug哈哈哈哈&#xff0c;说几个比较好的bug思想&#xff01; 这个页面算是我遇到的比较大的bug&#xff0c;因为我一开始的逻辑都写好了&#xff0c;询价就是在点击快递公司弹出弹框的时…

Conda配置完全指南——Windows系统Anaconda/Miniconda的安装、配置、基础使用、清理缓存空间和Pycharm/VSCode配置指南

本文同步发布在个人博客&#xff1a; Conda配置完全指南Conda 是一个开源的跨平台包管理与环境管理工具&#xff0c;广泛应用于数据科学、机器学习及 Python 开发领域。它不仅能帮助用户快速安装、更新和卸载第三方库&#xff0c;还能创建相互隔离的虚拟环境&#xff0c;解决不…

Quasar组件 Carousel走马灯

通过对比两个q-carousel组件来&#xff0c;了解该组件的属性 官方文档请参阅&#xff1a;Carousel 预览 源代码 <template><div class"q-pa-md"><div class"q-gutter-md"><q-carouselv-model"slide"transition-prev&quo…

风控域——风控决策引擎系统设计

摘要 本文详细介绍了风控决策引擎系统的设计与应用。决策引擎系统是一种智能化工具&#xff0c;可自动化、数据驱动地辅助或替代人工决策&#xff0c;广泛应用于金融、医疗、营销、风控等领域。文章阐述了决策引擎的核心功能&#xff0c;包括自动化决策、动态规则管理、实时处…

CAPL Class: TcpSocket (此类用于实现 TCP 网络通信 )

目录 Class: TcpSocketacceptopenclosebindconnectgetLastSocketErrorgetLastSocketErrorAsStringlistenreceivesendsetSocketOptionshutdown函数调用的基本流程服务器端的基本流程客户端的基本流程Class: TcpSocket学习笔记。来自CANoe帮助文档。 Class: TcpSocket accept /…

数据分析 —— 数据预处理

一、什么是数据预处理 数据预处理&#xff08;Data Preprocessing&#xff09;是数据分析和机器学习中至关重要的步骤&#xff0c;旨在将原始数据转换为更高质量、更适合分析或建模的形式。由于真实世界的数据通常存在不完整、不一致、噪声或冗余等问题&#xff0c;预处理可以…

软件架构风格系列(4):事件驱动架构

文章目录 前言一、从“用户下单”场景看懂事件驱动核心概念&#xff08;一&#xff09;什么是事件驱动架构&#xff1f;&#xff08;二&#xff09;核心优势&#xff1a;解耦与异步的双重魔法 二、架构设计图&#xff1a;三要素构建事件流转闭环三、Java实战&#xff1a;从简单…

arduino平台读取鼠标光电传感器

鼠标坏掉了&#xff0c;大抵是修不好了。&#xff08;全剧终—&#xff09; 但是爱动手的小明不会浪费这个鼠标&#xff0c;确认外观没有明显烧毁痕迹后&#xff0c;尝试从电路板上利用光电传感器进行位移的测量&#xff0c;光电传感器&#xff08;型号&#xff1a;FCT3065&am…

【Linux网络】网络层

网络层 在复杂的网络环境中确定一个合适的路径 IP 协议 IPV4 点分十进制[0,255].[0,255].[0,255].[0,255]IPV6 IP地址目标网格目标主机 基本概念 主机:配有IP地址,但是不进行路由控制的设备;路由器:即配有IP地址,又能进行路由控制;节点:主机和路由器的统称。 两个问题 路…

大模型学习:Deepseek+dify零成本部署本地运行实用教程(超级详细!建议收藏)

文章目录 大模型学习&#xff1a;Deepseekdify零成本部署本地运行实用教程&#xff08;超级详细&#xff01;建议收藏&#xff09;一、Dify是什么二、Dify的安装部署1. 官网体验2. 本地部署2.1 linux环境下的Docker安装2.2 Windows环境下安装部署DockerDeskTop2.3启用虚拟机平台…

LeetCode Hot100 (2、3、4、5、6、8、9、12)

题2--字母异或位分词 class Solution { public:vector<vector<string>> groupAnagrams(vector<string>& strs) {// 一开始的思路是&#xff0c;对于其中的一个单词&#xff0c;遍历所有排序组合&#xff0c;然后判断这些组合是否在哈希表里//&#xff0…

FastMCP:为大语言模型构建强大的上下文和工具服务

FastMCP&#xff1a;为大语言模型构建强大的上下文和工具服务 在人工智能快速发展的今天&#xff0c;大语言模型&#xff08;LLM&#xff09;已经成为许多应用的核心。然而&#xff0c;如何让这些模型更好地与外部世界交互&#xff0c;获取实时信息&#xff0c;执行特定任务&am…

数据结构(3)线性表-链表-单链表

我们学习过顺序表时&#xff0c;一旦对头部或中间的数据进行处理&#xff0c;由于物理结构的连续性&#xff0c;为了不覆盖&#xff0c;都得移&#xff0c;就导致时间复杂度为O&#xff08;n&#xff09;&#xff0c;还有一个潜在的问题就是扩容&#xff0c;假如我们扩容前是10…

Java Solon v3.3.0 发布(国产优秀应用开发基座)

Solon 框架&#xff01; Solon 是新一代&#xff0c;Java 企业级应用开发框架。从零开始构建&#xff08;No Java-EE&#xff09;&#xff0c;有灵活的接口规范与开放生态。采用商用友好的 Apache 2.0 开源协议&#xff0c;是“杭州无耳科技有限公司”开源的根级项目&#xff…

23种设计模式概述详述(C#代码示例)

文章目录 1. 引言1.1 设计模式的价值1.2 设计模式的分类 2. 面向对象设计原则2.1 单一职责原则 (SRP)2.2 开放封闭原则 (OCP)2.3 里氏替换原则 (LSP)2.4 接口隔离原则 (ISP)2.5 依赖倒置原则 (DIP)2.6 合成复用原则 (CRP)2.7 迪米特法则 (LoD) 3. 创建型设计模式3.1 单例模式 (…

数字化工厂升级引擎:Modbus TCP转Profinet网关助力打造柔性生产系统

在当今的工业自动化领域&#xff0c;通信协议扮演着至关重要的角色。Modbus TCP和Profinet是两种广泛使用的工业通信协议&#xff0c;它们分别在不同的应用场景中发挥着重要作用。然而&#xff0c;有时我们可能需要将这两种协议进行转换&#xff0c;以实现不同设备之间的无缝通…

FPGA生成随机数的方法

FPGA生成随机数的方法&#xff0c;目前有以下几种: 1、震荡采样法 实现方式一&#xff1a;通过低频时钟作为D触发器的时钟输入端&#xff0c;高频时钟作为D触发器的数据输入端&#xff0c;使用高频采样低频&#xff0c;利用亚稳态输出随机数。 实现方式二&#xff1a;使用三个…

【Linux C/C++开发】轻量级关系型数据库SQLite开发(包含性能测试代码)

前言 之前的文件分享过基于内存的STL缓存、环形缓冲区&#xff0c;以及基于文件的队列缓存mqueue、hash存储、向量库annoy存储&#xff0c;这两种属于比较原始且高效的方式。 那么&#xff0c;有没有高级且高效的方式呢。有的&#xff0c;从数据角度上看&#xff0c;&#xff0…