前言
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根据主角位置转换位置等功能
敬请期待我的下一个文章~~