Unity做一个类似星露谷物语的游戏【2】

前言

Hello and welcome back
今天我们来讲一下游戏场景中的工具,植物等物品,一些视觉效果比如在某些特定的植物旁边走过会带动它飘动

植物,工具,食物等的创建

我们首先要考虑这些物品有什么属性,比如这个物品的编号,它是什么类型的(是工具还是种子还是食物。。。),它的名字是什么,它对应的图片是什么等等,所以我们需要写一个类把它的全部属性写进去,请看下面的代码~ 这是我目前能想到游戏中一样物品的全部属性
从上到下依次是物品编号,物品类型,物品名字,物品图片,物品详细描述,物品的用处,物品的作用半径,是否为初始拥有的比如锄头斧头等,是否可以捡起来,是否可以丢弃,是否可以被吃掉,是否可以被携带

public class ItemDetial
{
    public int itemCode;
    public ItemType itemType;
    public string itemDescription;//描述
    public Sprite itemSprite;
    public string itemLogDescription;
    public short itemUseGridRadius;//特殊用处 水壶浇水,锄头犁地
    public float itemUseRadius;//作用半径
    public bool isStartingItem;
    public bool canBePickUp;//是否可以捡起
    public bool canBeDropped;//是否可以丢弃
    public bool canBeEaten;
    public bool canBeCarried;//是否随身携带
}
public enum ItemType
{
    Seed,
    Commodity,
    Watering_tool,
    Hoeing_tool,
    Chopping_tool,
    Breaking_tool,
    Reaping_tool,
    Collecting_tool,
    Reapable_scenary,//风景类
    Furniture,
    None,
    count//计数
}

现在我们知道了物品的信息,怎么把这些物品的信息存下来呢,让我来给大家介绍一个牛逼的类:
ScriptableObject 。继承了这个类就可以在工程目录右键创建出一个相应的asset文件,编辑这个文件就可以把数据存起来了~
如果大家不了解这个累的话可以看这位大佬的文章https://blog.csdn.net/qq_46044366/article/details/124310241
下面我们来写一个类来生成对应的文件

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "工具列表", menuName = "Scritable /Item/ItemList")]
public class OS_ItemList : ScriptableObject
{
    public List<ItemDetial> itemDetialList = new List<ItemDetial>();
}

这样我们右键->Creat->Scritable->Item->ItemList就可以生成一个存放ItemDetial的链表了
点击添加就可以编辑itemDetial了
在这里插入图片描述
(太多了要24个 我已经搞好了大家可以私信跟我要这个asset文件 或者来这里下载
https://gitee.com/yuan-miao/Start-Farme/tree/master/Assets/Spritable/Item
代码要自己敲哦,这个工程我还没有搞完 是个半成品)
这样我们的植物工具食物乱七八糟的就算搞好了!

数据全都有了我们现在要写一个管理类来管理这些数据
游戏场景建立一个空物体起名叫InventoryManager 然后添加组件InventoryManager
然后我们来写InventoryManager 的一些逻辑

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

public class InventoryManager : SingleMono<InventoryManager>
{
	private Dictionary<int, ItemDetial> itemDetialDic;
	[SerializeField] private OS_ItemList itemDetialList = null;//要手动托上去
	 private void CreateItemDetialDic()
   	 {
        itemDetialDic = new Dictionary<int, ItemDetial>();
        foreach (ItemDetial itemDetial in itemDetialList.itemDetialList)
        {
            itemDetialDic.Add(itemDetial.itemCode, itemDetial);
        }
     }
    public ItemDetial GetItemDetial(int itemCode)
   	 {
        ItemDetial itemDetial;
        if (itemDetialDic.TryGetValue(itemCode, out itemDetial))
            return itemDetial;
        return null;
   	 }
   	  override protected void Awake()
   	  {
        base.Awake();
        CreateItemDetialDic();
      }
}

来简单讲一下这个类的大概意思 收件创建一个存放itemDetial的字典,key是itemCode,value是itemDetial,我们要从刚才建立的asset文件获取数据 所以要获取itemDetialList,如果不想手动托的话可以把这个文件放到Resources文件夹中Load,我这里就手动托了
CreateItemDetialDic函数做的工作就是便利itemDetialList,把它们按照全部加到字典中
GetItemDetial函数做的工作就是按照itemcode来返回对应的itemDetail

物品类

刚才我们把物品全部的信息已经搞好了现在我们要创建物品基类的Prefab啦
首先我们要建立一个空的gameoobject 给它添加一个boxcollider2d 方便后续的碰撞检测,然后给它建立一个子物体添加组件spriteRenderer,注意这个选择的layer层要和玩家是统一图层,后续建设场景我们要分成很多层,所以这个物品一定要和玩家同一层级!!
然后我们在这个gameobject上添加脚本Item
Item代码奉上~

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

public class Item : MonoBehaviour
{
    [SerializeField]
    private int itemCode;
    public int ItemCode
    {
        get 
        { 
            return itemCode;
        }
        set 
        {
            itemCode = value;
        }
    }
    private SpriteRenderer spriteRenderer;
    private void Awake()
    {
        spriteRenderer = gameObject.GetComponentInChildren<SpriteRenderer>();
    }
    private void Start()
    {
        if (ItemCode != 0)
        {
            Init(ItemCode);
        }
    }
    private void Init(int itemCodeParam)
    {
        ItemCode = itemCodeParam;  
        ItemDetial itemDetial = InventoryManager.Instance.GetItemDetial(ItemCode);
        spriteRenderer.sprite = itemDetial.itemSprite; 
    } 
}

我们来简单讲一下这个代码
itemcode是不同物品的编号 给它加上SerializeField标签是为了可以在编译器上显示itemcode
InventoryManager.Instance.GetItemDetial(ItemCode)这个函数是根据Code来得到对应的itemDetail

做成之后是这个样子的
在这里插入图片描述

杂草,仙人掌随主角方向飘

既然植物已经做好了,那么我们可以来做个简单的功能,游戏中主角从某些植物旁边过去,会带起一阵风,让植物随风飘,我们也来实现以下这个功能
实现原理:原理很简单,我们要靠碰撞器,如果主角进入碰撞的位置x小于植物的位置x那么就是从左边进来的,植物就要向右飘 反之亦然,离开碰撞的原理也是一样的
既然知道这个原理 让我们来搞代码吧!

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

public class ItemNudge : MonoBehaviour
{
    private WaitForSeconds pause;
    private bool isAnimating = false;
    private void Awake()
    {
        pause = new WaitForSeconds(0.04f);
    }
    private void OnTriggerEnter2D(Collider2D collider)
    {
        if (isAnimating == false)
        {
            if (gameObject.transform.position.x < collider.gameObject.transform.position.x)
            {
                StartCoroutine(RotateAntiClock());
            }
            else
            {
                StartCoroutine(RotateClock());
            }
        }
    }
    private void OnTriggerExit2D(Collider2D collider)
    {
        if (isAnimating == false)
        {
            if (gameObject.transform.position.x > collider.gameObject.transform.position.x)
            {
                StartCoroutine(RotateAntiClock());
            }
            else
            {
                StartCoroutine(RotateClock());
            }
        }
    }
    IEnumerator RotateAntiClock()
    {
        isAnimating = true;
        for(int i = 0; i < 4; ++i)
        {
            gameObject.transform.GetChild(0).Rotate(0, 0, 2);//每0.04f旋转2度
            yield return pause;
        }
        for(int i = 0; i < 5; ++i)
        {
            gameObject.transform.GetChild(0).Rotate(0, 0, -2);//每0.04f旋转-2度
            yield return pause;
        }
        gameObject.transform.GetChild(0).Rotate(0, 0, 2);//复原
        yield return pause;
        isAnimating = false;
    }
    IEnumerator RotateClock()
    {
        isAnimating = true;
        for (int i = 0; i < 4; ++i)
        {
            gameObject.transform.GetChild(0).Rotate(0, 0, -2);//每0.04f旋转-2度
            yield return pause;
        }
        for (int i = 0; i < 5; ++i)
        {
            gameObject.transform.GetChild(0).Rotate(0, 0, 2);//每0.04f旋转2度
            yield return pause;
        }
        gameObject.transform.GetChild(0).Rotate(0, 0, -2);//复原
        yield return pause;
        isAnimating = false;
    }
}

来简单讲一下这个地方的代码
这个地方用到的知识点是携程,for循环4次是为了让他分成四步来实现左移或者右移,然后循环五次在循环是为了营造出一种飘来飘去的感觉然后复原,for循环的次数可以随便写,这个没有什么要求
然后我们可以手动的把这个代码拖到杂草上或者在Item代码中添加一个判断条件

    private void Init(int itemCodeParam)
    { 
        if (itemDetial.itemType == ItemType.Reapable_scenary)
            gameObject.AddComponent<ItemNudge>();
    }

因为我在asset文件中把可以飘动的物品的type设置的都是Reapable_scenary,所以可以直接在item初始化的时候让代码来添加

创建杂草

我们可以右键刚才创建的Item Prefab 选择Create->Prefab Variant
这样可以创建出一个Item的派生prefab 然后itemCode填asset设置的itemcode 我这里填的是10018
然后子物体的sprit找到Grass1 放到场景中从他旁边经过就可以实现刚才的功能了!
如果没有实现那可能是Player没有加碰撞体或者没有加Rigidbody2D
注意:Item的碰撞体是trigger类型!
在这里插入图片描述

在某些物品后面物品半透明的实现

在游戏中有那种很高的树 密密麻麻的如果很多,主角在树的后面就会被树遮到,所以我们可以给它添加一个半透明的功能,实现也和随风飘的功能一样,也是用到了碰撞体的触发器
代码奉上~

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SpriteRenderer))]
public class Fader : MonoBehaviour
{
    SpriteRenderer spriteRender;
    private void Awake()
    {
        spriteRender = gameObject.GetComponent<SpriteRenderer>();
    }
    public void FadeIn()
    {
        StartCoroutine(FadeInIenumer());
    }
    public void FadeOut()
    {
        StartCoroutine(FadeOutIenumer());
    }
    private IEnumerator FadeInIenumer()
    {
        float nowAlpha = spriteRender.color.a;
        float distance = nowAlpha - Settings.targetAlpha;
        while (nowAlpha - Settings.targetAlpha > 0.01f)
        {
            nowAlpha -= distance / Settings.fadeInSeconds * Time.deltaTime;
            spriteRender.color = new Color(1, 1, 1, nowAlpha);
            yield return new WaitForSeconds(0.01f);
        }
        spriteRender.color = new Color(1, 1, 1, Settings.targetAlpha);
    }
    private IEnumerator FadeOutIenumer()
    {
        float nowAlpha = spriteRender.color.a;
        float distance = 1-nowAlpha;
        while (1-nowAlpha> 0.01f)
        {
            nowAlpha += distance / Settings.fadeOutSeconds * Time.deltaTime;
            spriteRender.color = new Color(1, 1, 1, nowAlpha);
            yield return new WaitForSeconds(0.01f);
        }
        spriteRender.color = new Color(1, 1, 1,1);
    }
}

这里携程做的是让它慢慢渐隐到对应的color
树是分为树根 树枝等,我们要把这个脚本加到各个部位,Top,Trunk都要加 Tree没必要加,我不小心加上了,没影响
在这里插入图片描述
然后我们给Player添加一个脚本来触发Fader 中的FaderIn和FaderOut函数

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

public class TriggerFader : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        Fader[] faders = collision.gameObject.GetComponentsInChildren<Fader>();
        if (faders.Length > 0)
        {
            foreach(var fader in faders)
            {
                fader.FadeIn();
            }
        }
    }
    private void OnTriggerExit2D(Collider2D collision)
    {
        Fader[] faders = collision.gameObject.GetComponentsInChildren<Fader>();
        if (faders.Length > 0)
        {
            foreach (var fader in faders)
            {
                fader.FadeOut();
            }
        }
    }
}

这样我们碰到了树的trigger检测,就可以查找碰撞体子物体中有Fader组件的物体,然后进行透明处理了

结尾

物品相关的只是基本上就是这么多了,只要把物品信息全存进去,物品的逻辑就是要围绕这个字典来搞了,如果有不懂的地方欢迎私信问我哦
下一篇文章我们要来实现捡起物品,丢弃物品,选择物品高亮,物品UI根据主角位置转换位置等功能
敬请期待我的下一个文章~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值