前言
本文来实现一个类泰瑞利亚游戏的demo,其中主要包括经典的库存系统和建造系统
素材
人物
https://assetstore.unity.com/packages/2d/characters/warrior-free-asset-195707
瓦片
https://assetstore.unity.com/packages/2d/environments/platform-tile-pack-204101
一、建造系统
1. 定义物品类
游戏物品基类
using UnityEngine;
using UnityEngine.Tilemaps;
// 创建一个 ScriptableObject,用于表示游戏物品
[CreateAssetMenu(menuName = "GameObject/Item")]
public class Item : ScriptableObject
{
public TileBase tile; // 物品对应的瓦片
public Sprite image; // 物品的图像
public ItemType type; // 物品的类型
public ActionType actionType; // 物品的动作类型
public Vector2Int range = new Vector2Int(5, 4); // 物品的范围,默认为 5x4
}
// 定义枚举类型 ItemType,表示物品的类型
public enum ItemType
{
BuildingBlock, // 建筑块物品类型
Tool // 工具物品类型
}
// 定义枚举类型 ActionType,表示动作的类型
public enum ActionType
{
Dig, // 挖掘动作类型
Mine // 开采动作类型
}
using UnityEngine;
// 创建一个继承自 RuleTile 的自定义规则瓦片
[CreateAssetMenu(menuName = "Tiles/Custom Rule Tile")]
public class RuleTileWithData : RuleTile
{
public Item item; // 规则瓦片对应的物品数据
}
ps:RuleTileWithData 的意义在于扩展了 Unity 自带的 RuleTile 类,允许我们在规则瓦片中关联额外的物品数据(Item)。这样做的好处是,我们可以在使用规则瓦片的地图中,直接获取与特定瓦片关联的物品信息,而不需要额外的查找或维护数据结构。
添加游戏物品和RuleTileWithData
2. 绘制地图
简单绘制一个地图
3. 实现瓦片选中效果
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class BuildingSystem : MonoBehaviour
{
[SerializeField] private Item item; // 当前选中的物品
[SerializeField] private TileBase highlightTile; // 高亮显示瓦片所使用的 TileBase
[SerializeField] private Tilemap mainTilemap; // 主要的地图瓦片对象
[SerializeField] private Tilemap tempTilemap; // 临时地图瓦片对象,用于显示高亮瓦片
private Vector3Int highlightedTilePos; // 高亮显示的瓦片在网格中的位置
private bool highlighted; // 是否在高亮显示
private void Update()
{
// 如果当前有选中的物品,则在 Update 方法中更新高亮显示
if (item != null)
{
HighlightTile(item);
}
}
private Vector3Int GetMouseOnGridPos()
{
// 获取鼠标在当前网格中的位置
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3Int mouseCellPos = mainTilemap.WorldToCell(mousePos);
mouseCellPos.z = 0;
return mouseCellPos;
}
private void HighlightTile(Item currentItem)
{
// 获取鼠标在当前网格中的位置
Vector3Int mouseGridPos = GetMouseOnGridPos();
// 如果当前高亮显示的瓦片位置不等于鼠标位置,则更新高亮显示
if (highlightedTilePos != mouseGridPos)
{
// 清除之前高亮显示的瓦片
tempTilemap.SetTile(highlightedTilePos, null);
// 获取当前位置的瓦片,并在临时地图瓦片对象上高亮显示
TileBase tile = mainTilemap.GetTile(mouseGridPos);
if (tile)
{
tempTilemap.SetTile(mouseGridPos, highlightTile);
highlightedTilePos = mouseGridPos;
highlighted = true;
}
else
{
highlighted = false;
}
}
}
}
Main Tilemap绘制地图,Temp Tilemap用于显示选中框
挂载脚本,配置参数
效果
4. 限制瓦片选择
按Item里的range,控制瓦片只能在人物一定区域可选中
修改BuildingSystem
private Vector3Int playerPos; //玩家位置
//。。。
private void Update()
{
playerPos = mainTilemap.WorldToCell(transform.position);
// 如果当前有选中的物品,则在 Update 方法中更新高亮显示
if (item != null)
{
HighlightTile(item);
}
}
private void HighlightTile(Item currentItem)
{
// 获取鼠标在当前网格中的位置
Vector3Int mouseGridPos = GetMouseOnGridPos();
// 如果当前高亮显示的瓦片位置不等于鼠标位置,则更新高亮显示
if (highlightedTilePos != mouseGridPos)
{
// 清除之前高亮显示的瓦片
tempTilemap.SetTile(highlightedTilePos, null);
// 检查鼠标位置与玩家位置是否在范围内
if (InRange(playerPos, mouseGridPos, (Vector3Int)currentItem.range))
{
// 获取鼠标位置上的瓦片,并检查条件 GetTile获取指定坐标格子瓦片
if (CheckCondition(mainTilemap.GetTile<RuleTileWithData>(mouseGridPos), currentItem))
{
// 在临时地图瓦片对象上高亮显示瓦片
tempTilemap.SetTile(mouseGridPos, highlightTile);
highlightedTilePos = mouseGridPos;
highlighted = true;
}
else
{
highlighted = false;
}
}
else
{
highlighted = false;
}
}
}
//判断鼠标位置与玩家位置是否在范围内
private bool InRange(Vector3Int positionA, Vector3Int positionB, Vector3Int range)
{
// 判断两个位置之间的距离是否在范围内
Vector3Int distance = positionA - positionB;
if (Math.Abs(distance.x) >= range.x || Math.Abs(distance.y) >= range.y)
{
return false;
}
return true;
}
//检查瓦片与当前物品的条件是否匹配
private bool CheckCondition(RuleTileWithData tile, Item currentItem)
{
// 检查瓦片与当前物品的条件是否匹配
if (currentItem.type == ItemType.BuildingBlock)
{
if (!tile)
{
return true;
}
}
else if (currentItem.type == ItemType.Tool)
{
if (tile)
{
if (tile.item.actionType == currentItem.actionType)
{
return true;
}
}
}
return false;
}
效果
5. 放置物品功能
BuildingSystem新增功能
private void Update()
{
if (Input.GetMouseButtonDown(0))// 当玩家按下左键时
{
if (highlighted)
{
if (item.type == ItemType.BuildingBlock)// 如果当前选中的物品是建筑方块
{
Build(highlightedTilePos, item);// 放置方块
}
}
}
}
// 放置方块
private void Build(Vector3Int position, Item itemToBuild)
{
tempTilemap.SetTile(position, null);// 清除临时 Tilemap 上的方块
highlighted = false;// 取消高亮状态
mainTilemap.SetTile(position, itemToBuild.tile);// 在主 Tilemap 上放置方块
}
为了测试,先修改item类型为BuildingBlock
效果
6. 清除物品
private void Update()
{
if (Input.GetMouseButtonDown(0))// 当玩家按下左键时
{
if (highlighted)
{
if (item.type == ItemType.BuildingBlock)// 如果当前选中的物品是建筑方块
{
Build(highlightedTilePos, item);// 放置方块
}
else if (item.type == ItemType.Tool)// 如果当前选中的物品是工具
{
Destroy(highlightedTilePos);// 移除方块
}
}
}
}
// 移除方块以及生成相应物品
private void Destroy(Vector3Int position)
{
tempTilemap.SetTile(position, null);// 清除临时 Tilemap 上的方块
highlighted = false;// 取消高亮状态
RuleTileWithData tile = mainTilemap.GetTile<RuleTileWithData>(position);// 获取当前位置上的方块数据
mainTilemap.SetTile(position, null);// 在主 Tilemap 上移除方块
}
为了测试,先修改item类型为Tool
效果
7. 生成和拾取物品功能
新增Loot预制体,用于显示物品
新增脚本Loot
using System.Collections;
using UnityEngine;
public class Loot : MonoBehaviour
{
[SerializeField] private SpriteRenderer sr; // 用于显示物品图像的组件
[SerializeField] private new BoxCollider2D collider; // 触发器组件
[SerializeField] private float moveSpeed; // 拾取时的移动速度
private Item item; // 表示此物品的数据模型
// 初始化物品
public void Initialize(Item item)
{
this.item = item;
sr.sprite = item.image; // 显示物品图像
}
// 当进入触发器时执行的逻辑
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
StartCoroutine(MoveAndCollect(other.transform)); // 开始移动并拾取物品
}
}
// 移动并拾取物品的逻辑
private IEnumerator MoveAndCollect(Transform target)
{
Destroy(collider); // 拾取后销毁触发器
while (transform.position != target.position)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime); // 向目标移动
yield return 0;
}
Destroy(gameObject); // 拾取完成后销毁物品对象
}
}
挂载脚本,并配置参数
修改BuildingSystem生成物品
[SerializeField] private GameObject lootPrefab;// 拾取物品时生成的对象
// 移除方块以及生成相应物品
private void Destroy(Vector3Int position)
{
//。。。
Vector3 pos = mainTilemap.GetCellCenterWorld(position);// 获取方块中心的世界坐标
GameObject loot = Instantiate(lootPrefab, pos, Quaternion.identity);// 创建拾取物品
loot.GetComponent<Loot>().Initialize(tile.item);// 初始化拾取物品数据
}
记得挂载预制体,修改Player标签
运行效果
二、库存系统
待续
源码
整理好后我会放上来
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。点赞越多,更新越快哦!当然,如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~