Unity3D仿星露谷物语开发36之锄地动画2

1、目标

当角色锄地之后,地面会显示开垦后的样貌。

2、思路

上一篇中,虽然角色dig了hoe,同时grid属性也改变了,但是没有任何可视化的反馈。我们现在将添加新的功能,动态地将"dug ground"瓷砖添加到"GroundDecoration1"的tilemap上。

将使用如下的tileset进行绘制。

为了设置正确的dug ground tile,我们需要检查下周边的tiles是否被dug,此时存在16种组合方式。

第1、2种组合方式:

第3、4种组合方式:

第5、6种组合方式:

第7、8种组合方式:

第9、10种组合方式:

第11、12种组合方式:

第13、14种组合方式:

第15、16种组合方式:

连接周边grid的情况:

我们将增加"dug ground" tiles到GroundDecoration1 tilemaps。我们将标记GroundDecoration1和GroundDecoration2 tilemap层,以便可以快速定位到他们,这些标记放到Tag.cs脚本中。

修改GridPropertiesManager.cs脚本如下:

修改Player.cs脚本

3、修改Tag.cs脚本

cs 复制代码
public static class Tags
{
    public const string BoundsConfiner = "BoundsConfiner";
    public const string ItemsParentTransform = "ItemsParentTransform";

    public const string GroundDecoration1 = "GroundDecoration1";
    public const string GroundDecoration2 = "GroundDecoration2";
}

添加了下面的2个静态变量。

4、修改GridPropertiesManager.cs脚本

cs 复制代码
// 修改public为private
private Grid grid;

// 添加变量
private Tilemap groundDecoration1;
private Tilemap groundDecoration2;
[SerializeField] private Tile[] dugGround = null;

// 添加函数
 private void ClearDisplayGroundDecorations()
 {
     // Remove ground decorations
     groundDecoration1.ClearAllTiles();
     groundDecoration2.ClearAllTiles();
 }

 private void ClearDisplayGridPropertyDetails()
 {
     ClearDisplayGroundDecorations();
 }

private void ConnectDugGround(GridPropertyDetails gridPropertyDetails)
{
    // Select tile based on surrounding dug tiles
    Tile dugTile0 = SetDugTile(gridPropertyDetails.gridX, gridPropertyDetails.gridY);
    groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY, 0), dugTile0);

    // Set 4 tiles if dug surrounding current tile - up, down, left, right now that this central tile has been dug
    GridPropertyDetails adjacentGridPropertyDetails;

    adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY + 1);
    if(adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
    {
        Tile dugTile1 = SetDugTile(gridPropertyDetails.gridX, gridPropertyDetails.gridY + 1);
        groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY + 1, 0), dugTile1);
    }

    adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY - 1);
    if (adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
    {
        Tile dugTile2 = SetDugTile(gridPropertyDetails.gridX, gridPropertyDetails.gridY - 1);
        groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY - 1, 0), dugTile2);
    }

    adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX - 1, gridPropertyDetails.gridY);
    if (adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
    {
        Tile dugTile3 = SetDugTile(gridPropertyDetails.gridX - 1, gridPropertyDetails.gridY);
        groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX - 1, gridPropertyDetails.gridY, 0), dugTile3);
    }

    adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX + 1, gridPropertyDetails.gridY);
    if (adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
    {
        Tile dugTile4 = SetDugTile(gridPropertyDetails.gridX + 1, gridPropertyDetails.gridY);
        groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX + 1, gridPropertyDetails.gridY, 0), dugTile4);
    }
}


private Tile SetDugTile(int xGrid, int yGrid)
{
    // Get whether surrounding tiles(up,down,left,right) are dug or not
    bool upDug = IsGridSquareDug(xGrid, yGrid + 1);
    bool downDug = IsGridSquareDug(xGrid, yGrid - 1);
    bool leftDug = IsGridSquareDug(xGrid - 1, yGrid);
    bool rightDug = IsGridSquareDug(xGrid + 1, yGrid);

    #region Set appropriate tile based on whether surrounding tiles are dug or not
    if(!upDug && !downDug &&  !rightDug && !leftDug)
    {
        return dugGround[0];
    }
    else if(!upDug && downDug && rightDug && !leftDug)
    {
        return dugGround[1];
    }
    else if(!upDug && downDug && rightDug && leftDug)
    {
        return dugGround[2];
    }
    else if(!upDug && downDug && !rightDug && leftDug)
    {
        return dugGround[3];
    }
    else if(!upDug && downDug && !rightDug && !leftDug)
    {
        return dugGround[4];
    }
    else if(upDug && downDug && rightDug && !leftDug)
    {
        return dugGround[5];
    }
    else if(upDug && downDug && rightDug && leftDug)
    {
        return dugGround[6];
    }
    else if(upDug && downDug && !rightDug && leftDug)
    {
        return dugGround[7];
    }
    else if(upDug && downDug && !rightDug && !leftDug)
    {
        return dugGround[8];
    }
    else if(upDug && !downDug && rightDug && !leftDug)
    {
        return dugGround[9];
    }
    else if(upDug && !downDug && rightDug && leftDug)
    {
        return dugGround[10];
    }
    else if(upDug && !downDug && !rightDug && leftDug)
    {
        return dugGround[11];
    }
    else if(upDug && !downDug && !rightDug && !leftDug)
    {
        return dugGround[12];
    }
    else if(!upDug && !downDug && rightDug && !leftDug)
    {
        return dugGround[13];
    }
    else if(!upDug && !downDug && rightDug && leftDug)
    {
        return dugGround[14];
    }
    else if(!upDug && !downDug && !rightDug && leftDug)
    {
        return dugGround[15];
    }

    return null;

    #endregion Set appropriate tile based on whether surrounding tiles are dug or not

}


private bool IsGridSquareDug(int xGrid, int yGrid)
{
    GridPropertyDetails gridPropertyDetails = GetGridPropertyDetails(xGrid, yGrid);

    if(gridPropertyDetails == null)
    {
        return false;
    }
    else if(gridPropertyDetails.daysSinceDug > -1)
    {
        return true;
    }
    else
    {
        return false;
    }
}


private void DisplayGridPropertyDetails()
{
    // Loop throught all grid items
    foreach(KeyValuePair<string, GridPropertyDetails> item in gridPropertyDictionary)
    {
        GridPropertyDetails gridPropertyDetails = item.Value;

        DisplayDugGround(gridPropertyDetails);
    }
}


// 优化ISaveableRestoreScene函数
 public void ISaveableRestoreScene(string sceneName)
 {
     // Get sceneSave for scene - it exists since we created it in initialise
     if(GameObjectSave.sceneData.TryGetValue(sceneName, out SceneSave sceneSave))
     {
         // get grid property details dictionary - it exists since we created it in initialise
         if(sceneSave.gridPropertyDetailsDictionary != null)
         {
             gridPropertyDictionary = sceneSave.gridPropertyDetailsDictionary;
         }

         // If grid properties exist
         if(gridPropertyDictionary.Count > 0)
         {
             // grid property details found for the current scene destroy existing ground decoration
             ClearDisplayGridPropertyDetails();

             // Instantiate grid property details for current scene
             DisplayGridPropertyDetails();
         }
     }


 }

// 优化AfterSceneLoaded函数
private void AfterSceneLoaded()
{
    // Get Grid
    grid = GameObject.FindObjectOfType<Grid>();

    // Get tilemaps
    groundDecoration1 = GameObject.FindGameObjectWithTag(Tags.GroundDecoration1).GetComponent<Tilemap>();
    groundDecoration2 = GameObject.FindGameObjectWithTag(Tags.GroundDecoration2).GetComponent<Tilemap>();
}

完整的代码如下:

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Tilemaps;

[RequireComponent(typeof(GenerateGUID))]
public class GridPropertiesManager : SingletonMonobehaviour<GridPropertiesManager>, ISaveable
{
    private Tilemap groundDecoration1;
    private Tilemap groundDecoration2;

    private Grid grid;
    private Dictionary<string, GridPropertyDetails> gridPropertyDictionary;
    [SerializeField] private SO_GridProperties[] so_gridPropertiesArray = null;
    [SerializeField] private Tile[] dugGround = null;

    private string _iSaveableUniqueID;
    private GameObjectSave _gameObjectSave;


    public string ISaveableUniqueID { get { return _iSaveableUniqueID; } set { _iSaveableUniqueID = value; } }
    public GameObjectSave GameObjectSave { get { return _gameObjectSave; } set { _gameObjectSave = value; } }

    protected override void Awake()
    {
        base.Awake();

        ISaveableUniqueID = GetComponent<GenerateGUID>().GUID;
        GameObjectSave = new GameObjectSave();
    }

    private void OnEnable()
    {
        ISaveableRegister();

        EventHandler.AfterSceneLoadEvent += AfterSceneLoaded;
    }

    private void OnDisable() 
    {
        ISaveableDeregister();

        EventHandler.AfterSceneLoadEvent -= AfterSceneLoaded;
    }

    private void AfterSceneLoaded()
    {
        // Get Grid
        grid = GameObject.FindObjectOfType<Grid>();

        // Get tilemaps
        groundDecoration1 = GameObject.FindGameObjectWithTag(Tags.GroundDecoration1).GetComponent<Tilemap>();
        groundDecoration2 = GameObject.FindGameObjectWithTag(Tags.GroundDecoration2).GetComponent<Tilemap>();
    }

    public void ISaveableDeregister()
    {
        SaveLoadManager.Instance.iSaveableObjectList.Remove(this);
    }

    public void ISaveableRegister()
    {
        SaveLoadManager.Instance.iSaveableObjectList.Add(this);
    }

    public void ISaveableRestoreScene(string sceneName)
    {
        // Get sceneSave for scene - it exists since we created it in initialise
        if(GameObjectSave.sceneData.TryGetValue(sceneName, out SceneSave sceneSave))
        {
            // get grid property details dictionary - it exists since we created it in initialise
            if(sceneSave.gridPropertyDetailsDictionary != null)
            {
                gridPropertyDictionary = sceneSave.gridPropertyDetailsDictionary;
            }

            // If grid properties exist
            if(gridPropertyDictionary.Count > 0)
            {
                // grid property details found for the current scene destroy existing ground decoration
                ClearDisplayGridPropertyDetails();

                // Instantiate grid property details for current scene
                DisplayGridPropertyDetails();
            }
        }


    }

    public void ISaveableStoreScene(string sceneName)
    {
        // Remove sceneSave for scene
        GameObjectSave.sceneData.Remove(sceneName);

        // Create sceneSave for scene
        SceneSave sceneSave = new SceneSave();

        // create & add dict grid property details dictionary
        sceneSave.gridPropertyDetailsDictionary = gridPropertyDictionary;

        // Add scene save to game object scene data
        GameObjectSave.sceneData.Add(sceneName, sceneSave);
    }


    private void Start()
    {
        InitialiseGridProperties();
    }

    private void ClearDisplayGroundDecorations()
    {
        // Remove ground decorations
        groundDecoration1.ClearAllTiles();
        groundDecoration2.ClearAllTiles();
    }

    private void ClearDisplayGridPropertyDetails()
    {
        ClearDisplayGroundDecorations();
    }

    public void DisplayDugGround(GridPropertyDetails gridPropertyDetails)
    {
        // Dug
        if(gridPropertyDetails.daysSinceDug > -1)
        {
            ConnectDugGround(gridPropertyDetails);
        }
    }

    private void ConnectDugGround(GridPropertyDetails gridPropertyDetails)
    {
        // Select tile based on surrounding dug tiles
        Tile dugTile0 = SetDugTile(gridPropertyDetails.gridX, gridPropertyDetails.gridY);
        groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY, 0), dugTile0);

        // Set 4 tiles if dug surrounding current tile - up, down, left, right now that this central tile has been dug
        GridPropertyDetails adjacentGridPropertyDetails;

        adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY + 1);
        if(adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
        {
            Tile dugTile1 = SetDugTile(gridPropertyDetails.gridX, gridPropertyDetails.gridY + 1);
            groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY + 1, 0), dugTile1);
        }

        adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY - 1);
        if (adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
        {
            Tile dugTile2 = SetDugTile(gridPropertyDetails.gridX, gridPropertyDetails.gridY - 1);
            groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY - 1, 0), dugTile2);
        }

        adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX - 1, gridPropertyDetails.gridY);
        if (adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
        {
            Tile dugTile3 = SetDugTile(gridPropertyDetails.gridX - 1, gridPropertyDetails.gridY);
            groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX - 1, gridPropertyDetails.gridY, 0), dugTile3);
        }

        adjacentGridPropertyDetails = GetGridPropertyDetails(gridPropertyDetails.gridX + 1, gridPropertyDetails.gridY);
        if (adjacentGridPropertyDetails != null && adjacentGridPropertyDetails.daysSinceDug > -1)
        {
            Tile dugTile4 = SetDugTile(gridPropertyDetails.gridX + 1, gridPropertyDetails.gridY);
            groundDecoration1.SetTile(new Vector3Int(gridPropertyDetails.gridX + 1, gridPropertyDetails.gridY, 0), dugTile4);
        }
    }

    private Tile SetDugTile(int xGrid, int yGrid)
    {
        // Get whether surrounding tiles(up,down,left,right) are dug or not
        bool upDug = IsGridSquareDug(xGrid, yGrid + 1);
        bool downDug = IsGridSquareDug(xGrid, yGrid - 1);
        bool leftDug = IsGridSquareDug(xGrid - 1, yGrid);
        bool rightDug = IsGridSquareDug(xGrid + 1, yGrid);

        #region Set appropriate tile based on whether surrounding tiles are dug or not
        if(!upDug && !downDug &&  !rightDug && !leftDug)
        {
            return dugGround[0];
        }
        else if(!upDug && downDug && rightDug && !leftDug)
        {
            return dugGround[1];
        }
        else if(!upDug && downDug && rightDug && leftDug)
        {
            return dugGround[2];
        }
        else if(!upDug && downDug && !rightDug && leftDug)
        {
            return dugGround[3];
        }
        else if(!upDug && downDug && !rightDug && !leftDug)
        {
            return dugGround[4];
        }
        else if(upDug && downDug && rightDug && !leftDug)
        {
            return dugGround[5];
        }
        else if(upDug && downDug && rightDug && leftDug)
        {
            return dugGround[6];
        }
        else if(upDug && downDug && !rightDug && leftDug)
        {
            return dugGround[7];
        }
        else if(upDug && downDug && !rightDug && !leftDug)
        {
            return dugGround[8];
        }
        else if(upDug && !downDug && rightDug && !leftDug)
        {
            return dugGround[9];
        }
        else if(upDug && !downDug && rightDug && leftDug)
        {
            return dugGround[10];
        }
        else if(upDug && !downDug && !rightDug && leftDug)
        {
            return dugGround[11];
        }
        else if(upDug && !downDug && !rightDug && !leftDug)
        {
            return dugGround[12];
        }
        else if(!upDug && !downDug && rightDug && !leftDug)
        {
            return dugGround[13];
        }
        else if(!upDug && !downDug && rightDug && leftDug)
        {
            return dugGround[14];
        }
        else if(!upDug && !downDug && !rightDug && leftDug)
        {
            return dugGround[15];
        }

        return null;

        #endregion Set appropriate tile based on whether surrounding tiles are dug or not

    }

    private bool IsGridSquareDug(int xGrid, int yGrid)
    {
        GridPropertyDetails gridPropertyDetails = GetGridPropertyDetails(xGrid, yGrid);

        if(gridPropertyDetails == null)
        {
            return false;
        }
        else if(gridPropertyDetails.daysSinceDug > -1)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private void DisplayGridPropertyDetails()
    {
        // Loop throught all grid items
        foreach(KeyValuePair<string, GridPropertyDetails> item in gridPropertyDictionary)
        {
            GridPropertyDetails gridPropertyDetails = item.Value;

            DisplayDugGround(gridPropertyDetails);
        }
    }



    /// <summary>
    /// This initialises the grid property dictionary with the values from the SO_GridProperties assets and stores the values for each scene in
    /// GameObjectSave sceneData
    /// </summary>
    private void InitialiseGridProperties()
    {
        // loop through all gridproperties in the array
        foreach(SO_GridProperties so_GridProperties in so_gridPropertiesArray)
        {
            // Create dictionary of grid property details
            Dictionary<string, GridPropertyDetails> gridPropertyDictionary = new Dictionary<string, GridPropertyDetails>();

            // Populate grid property dictionary - Iterate through all the grid properties in the so gridproperties list
            foreach(GridProperty gridProperty in so_GridProperties.gridPropertyList)
            {
                GridPropertyDetails gridPropertyDetails;

                gridPropertyDetails = GetGridPropertyDetails(gridProperty.gridCoordinate.x, gridProperty.gridCoordinate.y, gridPropertyDictionary);

                if(gridPropertyDetails == null)
                {
                    gridPropertyDetails = new GridPropertyDetails();
                }

                switch (gridProperty.gridBoolProperty)
                {
                    case GridBoolProperty.diggable:
                        gridPropertyDetails.isDiggable = gridProperty.gridBoolValue;
                        break;
                    case GridBoolProperty.canDropItem:
                        gridPropertyDetails.canDropItem = gridProperty.gridBoolValue;
                        break;
                    case GridBoolProperty.canPlaceFurniture:
                        gridPropertyDetails.canPlaceFurniture = gridProperty.gridBoolValue;
                        break;
                    case GridBoolProperty.isPath:
                        gridPropertyDetails.isPath = gridProperty.gridBoolValue;
                        break;
                    case GridBoolProperty.isNPCObstacle:
                        gridPropertyDetails.isNPCObstacle = gridProperty.gridBoolValue;
                        break;

                    default:
                        break; 
                }

                SetGridPropertyDetails(gridProperty.gridCoordinate.x, gridProperty.gridCoordinate.y, gridPropertyDetails, gridPropertyDictionary);
            
            }

            // Create scene save for this gameobject
            SceneSave sceneSave = new SceneSave();

            // Add grid property dictionary to scene save data
            sceneSave.gridPropertyDetailsDictionary = gridPropertyDictionary;

            // If starting scene set the griProertyDictionary member variable to the current iteration
            if(so_GridProperties.sceneName.ToString() == SceneControllerManager.Instance.startingSceneName.ToString())
            {
                this.gridPropertyDictionary = gridPropertyDictionary;
            }

            // Add scene save to game object scene data
            GameObjectSave.sceneData.Add(so_GridProperties.sceneName.ToString(), sceneSave);
        }
    }

    /// <summary>
    /// Returns the gridPropertyDetails at the gridlocation fro the supplied dictionary,
    /// or null if no properties exist at that location
    /// </summary>
    /// <param name="gridX"></param>
    /// <param name="gridY"></param>
    /// <param name="gridPropertyDictionary"></param>
    /// <returns></returns>
    public GridPropertyDetails GetGridPropertyDetails(int gridX, int gridY, 
        Dictionary<string, GridPropertyDetails> gridPropertyDictionary)
    {
        // Construct key from coordinate
        string key = "x" + gridX + "y" + gridY;

        GridPropertyDetails gridPropertyDetails;

        // Check if grid property details exist for coordinate and retrieve
        if (!gridPropertyDictionary.TryGetValue(key, out gridPropertyDetails))
        {
            // if not found
            return null;
        }
        else
        {
            return gridPropertyDetails;
        }
    }

    public GridPropertyDetails GetGridPropertyDetails(int gridX, int gridY)
    {
        return GetGridPropertyDetails(gridX, gridY, gridPropertyDictionary);
    }

    /// <summary>
    /// Set the grid property details to gridPropertyDetails fro the tile at (gridX, gridY) for current scene
    /// </summary>
    /// <param name="gridX"></param>
    /// <param name="gridY"></param>
    /// <param name="gridPropertyDetails"></param>
    public void SetGridPropertyDetails(int gridX, int gridY, GridPropertyDetails gridPropertyDetails)
    {
        SetGridPropertyDetails(gridX, gridY, gridPropertyDetails, gridPropertyDictionary);
    }


    /// <summary>
    /// Set the grid property details to gridPropertyDetails for the title at (gridX, gridY) for the gridPropertyDictionary.
    /// </summary>
    /// <param name="gridX"></param>
    /// <param name="gridY"></param>
    /// <param name="gridPropertyDetails"></param>
    /// <param name="gridPropertyDictionary"></param>
    public void SetGridPropertyDetails(int gridX, int gridY, GridPropertyDetails gridPropertyDetails, Dictionary<string, GridPropertyDetails> gridPropertyDictionary)
    {
        // Construct key from coordinate
        string key = "x" + gridX + "y" + gridY;

        gridPropertyDetails.gridX = gridX;
        gridPropertyDetails.gridY = gridY;

        // Set value 
        gridPropertyDictionary[key] = gridPropertyDetails;
    }

}

5、修改Player.cs脚本

在HoeGroundAtCursorRoutine函数中添加如下代码:

6、设置绘图

(1)创建新的Palette

Create New Palette,并且命名为DugGround,保存到Assets -> Tilemap -> Tile Palettes下。

(2)创建新的Tiles

在Assets -> Sprites -> Sprite Textures -> Tile Sprites 下找到DugGround.

将DugGround拖到右边的Palette中,弹出的目录选择Assets -> Tilemap -> Tiles,创建新的目录Dug Ground,保存到该目录下。

左边灰色为Dug后的grid图案,右边银色为Water之后的grid图案。

7、配置Tile信息

点击Assets -> Tilemap -> Tiles -> Dug Ground,然后点击Open。

点击PersistentScene -> GridPropertiesManager,将Dug Ground中灰色的图片拖入到Dug Ground的List中。

8、添加Tag

点击GridPropertiesManager,点击Add Tag。

添加GroundDecoration1和GroundDecoration2两个Tag。

然后依次给Scene1/Scene2/Scene3场景Tilemap Grid下的GroundDecoration1和GroundDecoration2对象设置GroundDecoration1和GroundDecoration2两个Tag。

设置Prefabs -> Maps -> Tilemap Grid下的GroundDecoration1和GroundDecoration2对象设置GroundDecoration1和GroundDecoration2两个Tag。

9、运行游戏

相关推荐
bingbingyihao2 小时前
GPT对话UI--通义千问API
gpt·ui
Delphi菜鸟4 小时前
go+mysql+cocos实现游戏搭建
mysql·游戏·golang·gin·cocos2d
程序员爱钓鱼5 小时前
用 Go 写一个可以双人对弈的中国象棋游戏!附完整源码
游戏·go·游戏开发
zdsji5 小时前
从零开始物理引擎(六)- 重构完成与MVP理解
c++·算法·重构·ue5·游戏引擎
虾球xz6 小时前
游戏引擎学习第231天
c++·学习·算法·游戏引擎
李匠20247 小时前
C++游戏服务器开发之⑦redis的使用
c++·游戏
人生不过一瞬间11 小时前
Unity webgl 获取图片或视频数据
unity·lucene·webgl
活跃家族16 小时前
绕过UI的cooke和token的验证
ui
虾球xz17 小时前
游戏引擎学习第234天:实现基数排序
c++·学习·算法·游戏引擎