当前位置: 首页 > news >正文

Unity3D仿星露谷物语开发31之设置地面属性方法探索

1、目标

设置地面的属性,比如哪些地面是可以放置Items的,哪些地面可以挖洞。根据地面的这些属性,决定角色相应的动作能否顺利执行。

2、思路

我们的解决方案是:在Tilemaps中存储Boolean值信息

有些信息在设计阶段就需要进行设置:

  • Can we drop objects there?
  • Can we dig there?
  • Can we place furniture there?

另外一些信息在运行阶段进行设置:

  • Has the ground grid square(方格) been dug?
  • Has the ground grid square been watered?
  • Has a seed been planted in a ground grid square?

我们需要创建一个组件TilemapGridProperties附加到tilemap物体上。

该组件只在编辑模式下运行,并将Boolean值“yes/no - can I drop an item here”放到scriptable物体上。

每个Boolean的tilemap都会附加该组件。

3、创建新的Enum

通过枚举类GridBoolProperty定义地面的属性值,用来识别每个网格与哪个Bool属性相关。

public enum GridBoolProperty
{
    diggable,
    canDropItem,
    canPlaceFurniture,
    isPath,
    isNPCObstacle
}

4、创建GridCoordinate脚本

它是一个可序列化的向量,用于捕获网格坐标。

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

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

[System.Serializable]
public class GridCoordinate
{
    public int x;
    public int y;

    public GridCoordinate(int p1, int p2)
    {
        x = p1;
        y = p2; 
    }

    public static explicit operator Vector2(GridCoordinate gridCoordinate) 
    {
        return new Vector2( (float)gridCoordinate.x, (float)gridCoordinate.y );
    }

    public static explicit operator Vector2Int(GridCoordinate gridCoordinate)
    {   
        return new Vector2Int(gridCoordinate.x, gridCoordinate.y);
    }

    public static explicit operator Vector3(GridCoordinate gridCoordinate)
    {
        return new Vector3((float)gridCoordinate.x, (float)gridCoordinate.y, 0f);
    }

    public static explicit operator Vector3Int(GridCoordinate gridCoordinate)
    {
        return new Vector3Int(gridCoordinate.x, gridCoordinate.y, 0);
    }

}

public static explicit operator是显式类型转换的语法。

5、创建GridProperty脚本

在Assets -> Scripts -> Map下创建新的脚本命名为GridProperty。

这个就是每个Grid方格的属性类。

属性包含:坐标,地面属性的信息。

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

[System.Serializable]
public class GridProperty 
{
    public GridCoordinate gridCoordinate;     // 坐标
    public GridBoolProperty gridBoolProperty; // 地面属性
    public bool gridBoolValue = false;        // 是否Bool属性

    public GridProperty(GridCoordinate gridCoordinate, GridBoolProperty gridBoolProperty, bool gridBoolValue)
    {
        this.gridCoordinate = gridCoordinate;
        this.gridBoolProperty = gridBoolProperty;
        this.gridBoolValue = gridBoolValue;
    }
}

6、创建SO_GridProperties脚本

该脚本存储场景下的所有方格的GridProperty数据。

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

[CreateAssetMenu(fileName ="so_GridProperties", menuName ="Scriptable Objects/Grid Properties")]
public class SO_GridProperties : ScriptableObject
{
    public SceneName sceneName;
    public int gridWidth;
    public int gridHeight;
    public int originX;
    public int originY;

    [SerializeField]
    public List<GridProperty> gridPropertyList;
}

gridWidth/gridHeight是某一个场景的Tilemap的长宽。

originX/originY是tilemap左下角的坐标。

7、创建TilemapGridProperties脚本

在Tilemap Grid中,GridProperties对象下有对应地面属性的各种对象。

我们希望在编辑模式下,当打开GridProperties对象时,会收集Grid的信息;当关闭GridProperties对象时,会将收集的信息写入SO_GridProperties组件中。

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Tilemaps;

[ExecuteAlways]
public class TilemapGridProperties : MonoBehaviour
{
    private Tilemap tilemap;
    [SerializeField] private SO_GridProperties gridProperties = null;
    [SerializeField] private GridBoolProperty gridBoolProperty = GridBoolProperty.diggable;

    private void OnEnable()
    {
        // Only populate in the editor
        if( !Application.IsPlaying(gameObject))
        {
            tilemap = GetComponent<Tilemap>();

            if(gridProperties != null)
            {
                // 编辑模式下开启脚本,清空所有的gridPropertyList数据
                gridProperties.gridPropertyList.Clear();
            }
        }
    }

    private void OnDisable()
    {
        // Only populate in the editor
        if (!Application.IsPlaying(gameObject)) 
        {
            // 编辑模式下关闭脚本,存储数据
            UpdateGridProperties();

            if(gridProperties != null)
            {
                // This is required to ensure that the updated gridproperties gameobject gets saved when the game is saved - otherwise they are not saved.
                EditorUtility.SetDirty(gridProperties);
            }
        }
    }


    private void UpdateGridProperties()
    {
        // Compress tilemap bounds
        tilemap.CompressBounds();

        // Only populate in the editor
        if (!Application.IsPlaying(gameObject))
        {
            if(gridProperties != null)
            {
                Vector3Int startCell = tilemap.cellBounds.min;
                Vector3Int endCell = tilemap.cellBounds.max;

                for (int x = startCell.x; x < endCell.x; x++)
                {
                    for (int y = startCell.y; y < endCell.y; y++)
                    {
                        TileBase tile = tilemap.GetTile(new Vector3Int(x, y, 0));

                        if(tile != null)
                        {
                            gridProperties.gridPropertyList.Add(new GridProperty(new GridCoordinate(x, y), gridBoolProperty, true));
                        }
                    }
                }
            }
        }

    }

    private void Update()
    {
        // Only populate in the editor
        if (!Application.IsPlaying(gameObject) )
        {
            Debug.Log("DISABLE PROPERTY TILEMAPS");
        }
    }
}

在编辑模式下,开启脚本时会删除历史gridProperties数据,方便重新进行绘制;关闭脚本后,会遍历Tilemap实例对应的grid,并且存储数据。

运行时,需要提醒一定要关闭脚本,因为只有在关闭状态下Tilemap实例才有gridProperties数据。

8、创建so_GridProperties实例

在Assets -> Scriptable Object Assets下创建Maps目录:

在Maps下创建Scriptable Object命名为:so_GridProperties_Scene1_Farm

点击so_GridProperties_Scene1_Farm,点击Inspector,可以看到它的属性信息。

在Hierarchy中打开Scene1_Farm,然后再点击Tile Palette,选择Coordinate Brush,在Scene中鼠标移到左下角,可以看到originX/Y的坐标信息。

然后鼠标再拖到右上角,可以看到Pos和Size信息。这正是so_GridProperties_Scene1_Farm中要填的信息。

然后添加信息如下:

以相同的方法创建so_GridProperties_Scene2_Field,其属性信息如下:

再创建so_GridProperties_Scene2_Cabin,在Scene中测量如下:

其属性信息如下:

9、添加TilemapGridProperties脚本

在Scene3_Cabin中,展开GridProperties,其下的BoolXXX全部选中,然后将TilemapGridProperties脚本添加进去。

点击BoolCanPlaceFurniture,将so_GridProperties_Scene3_Cabin资源拖入到Grid Properties属性中,Grid Bool Property属性选择和Hirerachy中物体一致的名称。如下图所示:

再看一个例子如下:

逐一操作Hierarchy中的5个GridProperties物体。

然后同样的方法在Scene1_Farm和Scene2_Field中添加TilemapGridProperties脚本,并且添加对应的so_GridProperties物体,以及选择对应的GridBoolProperty属性。

10、测试so_GridProperties物体的GridPropertyList

通过上述步骤,在编辑模式下,打开Hierarchy -> SceneXXX -> GridProperties物体(OnEnable),绘制瓦片贴图。那么关闭GridProperties物体时(OnDisable),就会自动更新so_GridProperties_Scenexxx下Grid Property List的信息。

下面测试下该功能。

点击GridProperties,然后在Inspector中进行勾选。

然后点击BoolCanDropItem,选择一个瓦片,在Scene中进行添加。

接着,点击GridProperties,然后在Inspector中进行反勾选。

最后点击so_GridProperties_Scene3_Cabin,就可以看到Grid Property List下有一个元素。

11、小结

以上内容记录了Grid的属性。

我们通过so_GridProperties实例记录每个Scene的地面属性。它记录了瓦片地图的长宽、起始坐标位置(左下角位置),以及所有的Grid的属性列表。

怎么写这个so_GridProperties实例的数据呢?我们创建了TilemapGridProperties脚本,这个脚本在编辑模型下运行,然后挂载到每个Scene下的GridProperties物体的子物体上。每个子物体会遍历各自的Tilemap地图,然后把数据写到gridProperties中。而gridProperties就是so_GridProperties实例。可以看他的定义:

 [SerializeField] private SO_GridProperties gridProperties = null;

那么Grid有哪些属性?定义在GridProperty脚本中。

相关文章:

  • K8S学习之基础三十一:k8s中RBAC 的核心概念
  • MySQL的行级锁锁的到底是什么?
  • 深入探讨RAID 5的性能与容错能力:实验与分析(磁盘阵列)
  • 深入理解 IP、子网掩码、端口号和协议
  • 快速上手网络通信 -- Qt Network应用开发
  • vulhub/log4j2漏洞靶场----反弹shell
  • centos7安装时采用的默认分区(比如:/dev/sda3的对应挂载点是/),如何对系统扩容?
  • python的基本运用(六)(自定义函数def)
  • 特殊 IP 地址
  • 机器人交社保属于“无稽之谈”?
  • GAN生成对抗网络小记
  • 【leetcode100】括号生成
  • Linux内核,mmap_pgoff在mmap.c的实现
  • MyBatis SqlSessionFactory 是如何创建的?
  • Linux上位机开发实战(x86和arm自由切换)
  • java -jar 执行基于Maven构建的Java应用的方法总结
  • 剑指 Offer II 087. 复原 IP
  • RustDesk自建远程桌面服务教程
  • Vue与Django是如何传递参数的?
  • matlab 谐波分析公式绘图
  • 同济大学原常务副校长、著名隧道及地下工程专家李永盛逝世
  • 傅利叶提出下个十年战略,CEO顾捷:机器人要有温度,要用实际价值来定义形态
  • 印巴开始互袭军事目标,专家:冲突“螺旋式升级”后果或不可控
  • 兵韬志略|美2026国防预算未达1万亿,但仍寻求“暗度陈仓”
  • 新城市志|上海再攻坚,营商环境没有最好只有更好
  • 巴基斯坦军方:印度导弹袭击巴首都附近空军基地