【Unity实战】手戳一个自定义角色换装系统——2d3d通用

文章目录

每篇一句

你进步的速度,取决于你学习的速度,昨天的我,也跟今天的你一样。

前言

本文我们来手戳一个自定义角色换装系统,它包括基本的人物部位、颜色修改,并跨场景显示修改的人物信息,2d3d通用

最终效果

素材

链接:https://pan.baidu.com/s/1dubEMMBO-ZSm3gQWPkvmsA?pwd=5zi6

提取码:5zi6

开始

切换头型

新建PositionedSprite脚本,保存图片和位置位置

csharp 复制代码
using UnityEngine;

[System.Serializable]
public class PositionedSprite
{
    public Sprite Sprite; // Sprite对象
    public Vector3 PositionModifier; // 位置修正器
}

新建CustomizableElement脚本,控制图片切换

csharp 复制代码
using System.Collections.Generic;
using UnityEngine;

public class CustomizableElement : MonoBehaviour
{
    [SerializeField]
    private SpriteRenderer _spriteRenderer; // Sprite渲染器

    [SerializeField]
    private List<PositionedSprite> _spriteOptions; // 可选的Sprite列表

    [SerializeField]
    private List<Color> _colorOptions; // 可选的颜色列表

    [field: SerializeField]
    public int ColorIndex { get; set; } // 颜色索引

    [field: SerializeField]
    public int SpriteIndex { get; private set; } // Sprite索引

    [ContextMenu(itemName: "下一个图片")]
    public PositionedSprite NextSprite()
    {
        SpriteIndex = Mathf.Min(SpriteIndex + 1, _spriteOptions.Count - 1); // 切换到下一个Sprite
        UpdateSprite();
        return _spriteOptions[SpriteIndex];
    }

    [ContextMenu(itemName: "上一个图片")]
    public PositionedSprite PreviousSprite()
    {
        SpriteIndex = Mathf.Max(SpriteIndex - 1, 0); // 切换到上一个Sprite
        UpdateSprite();
        return _spriteOptions[SpriteIndex];
    }

    private void UpdateSprite()
    {
    	if(_spriteOptions.Count == 0) return;
        SpriteIndex = Mathf.Clamp(SpriteIndex, 0, _spriteOptions.Count - 1); // 限制Sprite索引在合法范围内
        var positionedSprite = _spriteOptions[SpriteIndex];
        _spriteRenderer.sprite = positionedSprite.Sprite; // 更新Sprite
        transform.localPosition = positionedSprite.PositionModifier; // 更新位置
    }
}

挂载并配置参数

效果

添加更改颜色

csharp 复制代码
[ContextMenu(itemName: "下一个颜色")]
public Color NextColor()
{
    ColorIndex = Mathf.Min(ColorIndex + 1, _colorOptions.Count - 1); // 切换到下一个颜色
    UpdateColor();
    return _colorOptions[ColorIndex];
}

[ContextMenu(itemName: "上一个颜色")]
public Color PreviousColor()
{
    ColorIndex = Mathf.Max(ColorIndex - 1, 0); // 切换到上一个颜色
    UpdateColor();
    return _colorOptions[ColorIndex];
}

private void UpdateColor()
{
	if(_colorOptions.Count == 0) return;
    _spriteRenderer.color = _colorOptions[ColorIndex]; // 更新颜色
}

配置

效果

随机控制头型和颜色

csharp 复制代码
[ContextMenu(itemName: "随机化")]
public void Randomize()
{
    SpriteIndex = Random.Range(0, _spriteOptions.Count); // 随机选择一个Sprite索引
    ColorIndex = Random.Range(0, _colorOptions.Count); // 随机选择一个颜色索引
    UpdateSprite();
    UpdateColor();
}

效果

新增眼睛

同理,添加眼睛,并配置眼睛的位置

效果

同样的方法配置人物的其他部位

效果

设置相同颜色部位

可能我们不希望所有部位都真的随机变色,比如我希望角色的肤色颜色保持一致

修改代码

csharp 复制代码
[SerializeField]
private List<SpriteRenderer> _copyColorTo; // 需要拷贝颜色的SpriteRenderer列表

// ...

private void UpdateColor()
{
   if(_colorOptions.Count == 0) return;
    var newColor = _colorOptions[ColorIndex]; // 获取新的颜色
   _spriteRenderer.color = newColor; // 更新当前SpriteRenderer的颜色

   // 将颜色拷贝到其他SpriteRenderer
   _copyColorTo.ForEach(sr => sr.color = newColor);
}

配置

当我们更新头颜色时,手会同步更新,保持头跟手颜色一样

效果

全部部位随机

新增CustomizationRandomizer 代码

csharp 复制代码
using UnityEngine;

public class CustomizationRandomizer : MonoBehaviour
{
    [ContextMenu(itemName: "随机全部")]
    public void Randomize()
    {
        var elements = GetComponentsInChildren<CustomizableElement>(); // 获取所有CustomizableElement组件

        foreach (var element in elements)
        {
            element.Randomize(); // 调用CustomizableElement的Randomize方法,随机化每个元素
        }
    }
}

挂载脚本,效果

绘制UI并添加点击事件

效果

通过代码控制点击事件

一个个配置按钮事件太麻烦了,我们可以通过脚本来控制

csharp 复制代码
public class UI_CustomizationPicker : MonoBehaviour
{
    [SerializeField]
    private CustomizableElement _customizableElement; // 自定义元素对象
    [SerializeField]
    private Button _previousSpriteButton;
    [SerializeField]
    private Button _nextSpriteButton;
    [SerializeField]
    private TMP_Text _spriteId;

    private void Start()
    {
		UpdateSpriteId();
        // 为按钮添加点击事件
        _previousSpriteButton.onClick.AddListener(() =>
        {
            _customizableElement.PreviousSprite(); // 切换到上一个Sprite
            UpdateSpriteId(); // 更新Sprite的ID文本
        });

        _nextSpriteButton.onClick.AddListener(() =>
        {
            _customizableElement.NextSprite(); // 切换到下一个Sprite
            UpdateSpriteId(); // 更新Sprite的ID文本
        });
    }

    private void UpdateSpriteId()
    {
        // 更新Sprite的ID文本
        _spriteId.SetText(_customizableElement.SpriteIndex.ToString().PadLeft(2, '0'));
    }
}

配置参数

效果

添加颜色修改的事件

修改CustomizableElement获取当前的颜色

csharp 复制代码
public Color CurrentColor => _colorOptions.Count == 0 ? Color.white : _colorOptions[ColorIndex]; //获取当前的颜色

修改UI_CustomizationPicker,添加颜色切换事件

csharp 复制代码
[SerializeField]
private Button _previousColorButton;
[SerializeField]
private Button _nextColorButton;
[SerializeField]
private Image _colorIcon;

private void Start()
{
    // 。。。
    if(_colorIcon != null){
	    _previousColorButton.onClick.AddListener(() =>
	    {
	        _customizableElement.PreviousColor();
	        UpdateColorIcon();
	    });
	
	    _nextColorButton.onClick.AddListener(() =>
	    {
	        _customizableElement.NextColor();
	        UpdateColorIcon();
	    });
	 }
}

private void UpdateColorIcon()
{
    _colorIcon.color = _customizableElement.CurrentColor;
}

效果

其他部位效果UI切换

跟前面一样配置各个部位的切换即可,配置其他部位最终效果

效果

添加随机按钮

新增脚本UI_CustomizationUI,定义随机按钮事件

csharp 复制代码
public class UI_CustomizationUI : MonoBehaviour
{
    private List<UI_CustomizationPicker> _pickers; // 自定义选择器列表

    void Start()
    {
        _pickers = GetComponentsInChildren<UI_CustomizationPicker>().ToList(); // 获取所有自定义选择器组件并转换为列表
    }

    // 更新所有选择器的状态
    public void UpdatePickersState()
    {
        _pickers.ForEach(picker =>
        {
        	picker._customizableElement.Randomize();//随机修改
            picker.UpdateSpriteId(); // 更新Sprite的ID文本
            picker.UpdateColorIcon(); // 更新颜色图标
        });
    }
}

挂载脚本

效果

保存角色变更数据

新增CustomizationType 脚本,定义不同部位类型

csharp 复制代码
using UnityEngine;

[CreateAssetMenu]
public class CustomizationType : ScriptableObject
{
    
}

添加各个部位配置

新增CustomizationData脚本,定义各种数据

csharp 复制代码
using UnityEngine;
using System;

[Serializable]
public class CustomizationData
{
    // 使用情况:指定自定义类型
    [field:SerializeField]
    public CustomizationType Type { get; private set; }

    // 使用情况:指定带位置的精灵
    [field:SerializeField]
    public PositionedSprite Sprite { get; private set; }

    // 使用情况:指定颜色
    [field:SerializeField]
    public Color Color { get; private set; }

    // CustomizationData 类的构造函数
    public CustomizationData(CustomizationType t, PositionedSprite s, Color c)
    {
        Type = t;
        Sprite = s;
        Color = c;
    }
}

修改CustomizableElement

csharp 复制代码
[SerializeField]
private CustomizationType _type;

public CustomizationData GetCustomizationData(){
    return new CustomizationData(_type, _spriteOptions[SpriteIndex], _spriteRenderer.color);
}

绑定对应数据

新增CustomizedCharacter,保存人物各个部位数据

csharp 复制代码
using UnityEngine;
using System.Collections.Generic;

[CreateAssetMenu]
public class CustomizedCharacter : ScriptableObject
{
    // Usage: 包含所有自定义数据的列表
    [field:SerializeField]
    public List<CustomizationData> Data { get; private set; }

    // 收集所有可定制元素的自定义数据
    public void GatherCustomizationData()
    {
        // 查找场景中的所有可定制元素
        var customizableElements = FindObjectsOfType<CustomizableElement>();

        // 创建一个新的自定义数据列表
        Data = new List<CustomizationData>();

        // 遍历所有可定制元素并添加它们的自定义数据到列表中
        foreach (var element in customizableElements)
        {
            Data.Add(element.GetCustomizationData());
        }
    }
} 

新增人物配置

新增CustomizableCharacter

csharp 复制代码
using UnityEngine;

public class CustomizableCharacter : MonoBehaviour
{
    [SerializeField]
    private CustomizedCharacter _character;

    // Usage: 在编辑器中的上下文菜单中添加 "Randomize All" 选项
    [ContextMenu(itemName: "Randomize All")]
    // Usage: 随机化所有可定制元素的外观
    public void Randomize()
    {
        // 获取所有子物体中的 CustomizableElement 组件数组
        var elements = GetComponentsInChildren<CustomizableElement>();

        // 遍历每个可定制元素,随机化其外观
        foreach (var element in elements)
        {
            element.Randomize();
        }
    }

    // Usage: 存储定制信息
    public void StoreCustomizationInformation()
    {
        // 获取所有子物体中的 CustomizableElement 组件数组
        var elements = GetComponentsInChildren<CustomizableElement>();

        // 清空已有的定制数据
        _character.Data.Clear();

        // 遍历每个可定制元素,获取其定制数据并添加到角色的数据列表中
        foreach (var element in elements)
        {
            _character.Data.Add(element.GetCustomizationData());
        }
    }
}

挂载脚本,配置数据

新增按钮用于跳转和保持角色数据

效果,数据被保存在了Player里

跳转场景显示角色数据

新增CustomizedCharacterElement脚本,用来渲染角色各个部位的图片和颜色

csharp 复制代码
using UnityEngine;
using System.Linq;

public class CustomizedCharacterElement : MonoBehaviour
{
    // Usage: 指定该元素的自定义类型
    [field:SerializeField]
    public CustomizationType Type { get; private set; }

    // Usage: 指定所属的自定义角色
    [SerializeField]
    private CustomizedCharacter _character;

    private SpriteRenderer _spriteRenderer;

    // Start 方法在对象实例化时调用
    private void Start()
    {
        // 获取 SpriteRenderer 组件
        _spriteRenderer = GetComponent<SpriteRenderer>();

        // 查找自定义角色中指定类型的自定义数据
        var customization = _character.Data.FirstOrDefault(d => d.Type == Type);

        // 如果找不到匹配的自定义数据,则返回
        if (customization == null)
        {
            return;
        }

        // 应用自定义数据中的颜色和精灵到元素上
        _spriteRenderer.color = customization.Color;
        _spriteRenderer.sprite = customization.Sprite.Sprite;

        // 应用自定义数据中的位置修正器到元素上
        transform.localPosition = customization.Sprite.PositionModifier;
    }
}

新建场景,挂载脚本,添加配置角色属性

修改CustomizableCharacter脚本,添加跳转场景方法

csharp 复制代码
//跳转场景
SceneManager.LoadScene("Game");

效果

源码

为了防止大家变懒,源码就不提供了,大家直接可以照着文章思路进行学习

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。点赞越多,更新越快哦!当然,如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关推荐
starsongda6 小时前
科技成果跃然“屏”上,虚拟展厅引领科技展示新风尚
科技·3d·虚拟现实
梦想的理由7 小时前
3D人体建模的前沿探索(二):深入解析SMPL-IK与多视角人体网格重建
3d
charon877810 小时前
UE ARPG | 虚幻引擎战斗系统
游戏引擎
道可云11 小时前
道可云人工智能&元宇宙每日资讯|2024国际虚拟现实创新大会将在青岛举办
大数据·人工智能·3d·机器人·ar·vr
小春熙子11 小时前
Unity图形学之Shader结构
unity·游戏引擎·技术美术
Sitarrrr14 小时前
【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
3d·unity
极梦网络无忧14 小时前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
逐·風1 天前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
_oP_i1 天前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
代码盗圣1 天前
GODOT 4 不用scons编译cpp扩展的方法
游戏引擎·godot