【用unity实现100个游戏之13】复刻类泰瑞利亚生存建造游戏

2023-09-21 11:34:26

前言

本文来实现一个类泰瑞利亚游戏的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。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

更多推荐

自然语言处理应用(三):微调BERT

微调BERT微调(Fine-tuning)BERT是指在预训练的BERT模型基础上,使用特定领域或任务相关的数据对其进行进一步训练以适应具体任务的需求。BERT(BidirectionalEncoderRepresentationsfromTransformers)是一种基于Transformer架构的深度双向预训练语

机器学习 day35(决策树)

决策树上图的数据集是一个特征值X采用分类值,即只取几个离散值,同时也是一个二元分类任务,即标签Y只有两个值上图为之前数据集对应的决策树,最顶层的节点称为根节点,椭圆形节点称为决策节点,矩形节点称为叶子节点决策树学习算法的工作是,在所有可能的决策树中,选择一个在训练集上能表现良好,并能很好的推广到新数据(即交叉验证集和测

CUDA小白 - NPP(9) 图像处理 Statistical Operations

cuda小白原始API链接NPPGPU架构近些年也有不少的变化,具体的可以参考别的博主的介绍,都比较详细。还有一些cuda中的专有名词的含义,可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》常见的NppStatus,可以看这里。本文主要介绍的是NPP的统计

今天开课,欢迎学习!公益课“14天学会统计学与SPSS”,课程结束赠送全套视频...

新一期的课程今天开课!来学习吧!我们精心整理的600页彩色纸质版教程就可以帮助你更好得掌握本课程!“14天学会医学统计学与SPSS”,是浙江中医药大学医学统计教研室、浙江省预防医学会卫生统计学专业委员会共同主持的公益网课!不是忽悠人的商业收费课程!高校公开课,免费的!自2021年起,浙江中医药大学医学统计教研室教研书主

【ijkplayer】编译 Android 版本的 ijkplayer ⑦ ( 使用 AS 打开源码 | 重新设置 AGP 和 Gradle 版本号 | 设置依赖仓库 | 设置依赖 | 编译运行 )

文章目录一、AndroidStudio打开编译后的ijkplayer源码二、重新设置AndroidGradle插件版本号和Gradle构建工具版本号三、设置依赖仓库1、取消jcenter仓库2、添加google和mavenCentral仓库3、添加阿里云仓库四、取消jcenter上传相关插件五、设置编译工具版本号六、取

Tomcat调优【精简版】

Tomcat调优优化Tomcat内存分配调整Tomcat启动脚本contalina.sh,设置tomcat启动时分配的内存很可使用的最大内存;CATALINA_OPTS调整Tomcat线程池Tomcat默认使用的线程池:ThreadPoolExecutor可以通过修改server.xml的Connector节点下的ma

在Android Studio中,如何通过CMake 配置文件来实现多个动态依赖库的编译?

在AndroidStudio中,如何通过CMake配置文件来实现多个动态依赖库的编译?Author:LycanNote:以下问题解答通过大模型生成,主要用于个人学习和备忘,仅供参考,若有错误或者侵权,请联系我修正,谢谢。问题在AndroidStudio中,如何通过CMake配置文件来实现多个动态依赖库的编译?请一步一步

离散高斯抽样(Discrete Gaussian Sampling)

离散高斯抽样离散高斯抽样(DiscreteGaussianSampling)是一种常见于密码学和数学领域的随机采样方法。它通常用于构建基于格(lattice)的密码学方案,如基于格的加密和数字签名。DiscreteGaussianSampling的主要目的是从一个离散的集合中随机选择元素,同时遵循高斯分布(Gaussi

C# 随机数生成 Mersenne Twister 马特赛特旋转演算法 梅森旋转算法

NuGet安装MathNet.Numerics引用:usingMathNet.Numerics.Random;///<summary>///包括lower,不包括upper///</summary>///<paramname="lower"></param>///<paramname="upper"></param>/

vue3 的CreateApp

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录从一个例子开始从一个例子开始constHelloVueApp={data(){return{message:'HelloVue!'}}}Vue.createApp(HelloVueApp).mount('#he

【Acorn】JS解析器编译原理

Acorn是什么?Acorn是一个用JavaScript编写的解析器,专门用于将源代码解析为抽象语法树(AbstractSyntaxTree,AST)。它是一个轻量级、高性能的解析器,被广泛应用于许多JavaScript工具和框架中。Acorn的整体工作流程输入源代码(InputSourceCode):接收JavaSc

热文推荐