Excel与Unity工作流(二):基础对话框架

这篇具有很好参考价值的文章主要介绍了Excel与Unity工作流(二):基础对话框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

本文将演示在unity中实现类似galgame的对话效果,并且通过Excel进行文本、图片、选项、赋值、音乐的配置

对话框架效果示意图

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#
(该图主要是展示版面和大致目标效果,与本文关系不大) (来源:《无期迷途》)

Excel表格总览

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

整体框架与主要思路

要实现的效果

每点击一次鼠标,就出现下一个对话/或者出现选项;

如果出现选项,点击选项,会有不同的对话;

对话的时候人物的立绘会有改变;

可以进行简单的数值判断与赋值;

可以播放音乐与音效

整体思路

通过标志与ID来决定下一个对话播放第几个

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

标志位

"#"号是普通的对话行,屏幕里会出现名字与人物

"&"号是选项,这一行会以一个选项的方式出现,内容会以选项上的文字表现

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#
(示意图,来源:《无期迷途》)

以上图举例,最底下的文字就是用"#"号行来表现;右侧的两个选项通过两行"&"号表现,选项上的文字就是那两行"内容"列里的文字

"end"标志符就是结束整段对话

跳转

每一行都有自己的ID,也有"跳转"列。当这一行结束后(一般通过点击继续放下一个),会跳转到"跳转列"的ID所在的行

效果与判定

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

判定:在上图ID为2的行里,"判定"那一列有"好感度>10"的文字,如果此时好感度>=10,那么之后会跳转到5(跳转列里"&"符号前面的那一个数字),否则就是跳转到4

效果:在上图ID为3-5的行里,有"好感度+-10"的文字,"+"用来区分数值名与数值改变量,所以是"好感度"这个数值,进行"-10"的操作

 *号行

如果需要多个条件都满足才能跳转到下一行呢?

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

ID为7的"是时候打开网抑云了"需要时间晚于23:00,并且活力要大于20,此时我们发现一行只写一个判定不够

故引入"*"号行。这个标志符所代表的行不会出现在游戏的画面上,不需要玩家点击才继续,而是自动触发并自动跳转到下一个要跳转的行。

"*"号行可以进行效果赋值、条件判断以及人物清空(这在下文的"人物"板块会提到)、背景替换等等功能

"*"号行可以多个混合使用,看具体实现情况而定

人物

在对话时,我们需要人物立绘来展现效果(进行激烈的立绘碰撞 bushi)

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#
(示意图,来源:《无期迷途》)

在Excel示例表中,主要通过"人物立绘图片","人物位置"进行控制

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

"人物立绘图片"由人物的底+人物表情组成,通过"&"符号进行分隔

"人物位置"Left就是在左边出现,Right就是在右边出现,Clear会清除这个人所有的立绘

音乐音效

通过"背景音乐"和"音效"控制

"背景音乐"会循环播放

"音效"只播放一次

实现

导入

在写完需要导入的剧情表格后,另存为csv格式(注意保存为 UTF-8编码)

这一部分与本系列第一篇类似,这里直接贴代码

Excel与Unity工作流(一):使用Excel进行属性配置-CSDN博客

public void SetTextAsset(string ResourcesPath)
    {
        textAsset = Resources.Load<TextAsset>(ResourcesPath);
        textAssetPath = ResourcesPath;
    }

public void ReadText(TextAsset _textAsset)
    {        
        dialogRows = null;        
        dialogRows = _textAsset.text.Split('\n');

        cells = new string[dialogRows.Length][];
        for (int i = 0; i < dialogRows.Length; i++)
        {
            cells[i] = dialogRows[i].Split(',');
        }
    }

对话框架

具体的unity内部图片与按钮放置可以看参考链接视频(本文的对话框架就是基于那个视频进行的修改添加)

https://www.bilibili.com/video/BV1v5411D79x/文章来源地址https://www.toymoban.com/news/detail-851274.html

string textAssetPath;
    [SerializeField] TextAsset textAsset;//输入的文本文件CSU

    [Header("分支选项")]
    [SerializeField] GameObject OptionalButton;
    [SerializeField] Transform buttonGroup;


    [Header("普通对话")]
    [SerializeField] GameObject chatPanel;
    [SerializeField] GameObject talkBox;
    TMP_Text dialogText;
    [SerializeField] Image backGroundPic;

    int dialogIndex = 0;
    int beginID = 0;

    string[][] cells;
    string[] dialogRows;
    bool isBegin;
    bool canNext;
    public void BeginChat()
    {
        {
            chatPanel.transform.Find("Sprite").gameObject.SetActive(true);
            talkBox.gameObject.SetActive(true);

            //把所有图片换成透明(具体方法在下文的"图片"中)
            ClearAllPic();
            ClearAllChoices();
            ClearImageLiHui();

        }
        print("BeginChat" + textAssetPath + "beginID" + beginID);
        isBegin = true;
        //这里的TextAsset会为null
        if (textAsset == null)
        {
            SetTextAsset(textAssetPath);
        }
        ReadText(textAsset);
        dialogIndex = beginID;
        ShowDialogRow();
        canNext = true;
    }


    public void ShowDialogRow()
    {
        for (int i = 1; i < dialogRows.Length; i++)//遍历来寻找正确的一行 首行是列名当然不含信息 所以i从1开始
        {
            if (int.Parse(cells[i][1]) == dialogIndex)
            {
                //播放音乐
                if (cells[i].Length > 10)
                {
                    if (cells[i][10] != "")
                    {
                        PlayBackGroundMusic(cells[i][10]);
                    }
                }

                //播放音效
                if (cells[i].Length > 11)
                {
                    if (cells[i][11] != "")
                    {
                        print("PlayEffectMusic" + cells[i][11]);
                        PlayEffectMusic(cells[i][11]);
                    }
                }

                if (cells[i][0] == "*")
                {

                    UpdateBackGround(cells[i][9]);
                    UpdateImage(cells[i][7], cells[i][8]);
                    if (cells[i][5] != "")
                    {
                        OptionEffect(cells[i][5]);
                    }


                    if (cells[i][4].Contains('&'))
                    {
                        string[] judge = cells[i][6].Split('>');
                        string[] jump = cells[i][4].Split('&');
                        OptionJudge(judge[0], judge[1], int.Parse(jump[0]), int.Parse(jump[1]));
                    }
                    else
                    {
                        dialogIndex = int.Parse(cells[i][4]);
                        ShowDialogRow();
                    }
                    break;
                }
                if (cells[i][0] == "#")
                {
                    UpdateBackGround(cells[i][9]);

                    UpdateImage(cells[i][7], cells[i][8]);
                    UpdateText(cells[i][2], cells[i][3]);


                    if (cells[i][5] != "")
                    {
                        OptionEffect(cells[i][5]);
                    }
                    if (cells[i][4].Contains('&'))
                    {
                        string[] judge = cells[i][6].Split('>');
                        string[] jump = cells[i][4].Split('&');
                        OptionJudge(judge[0], judge[1], int.Parse(jump[0]), int.Parse(jump[1]));
                    }
                    else
                    {
                        dialogIndex = int.Parse(cells[i][4]);
                    }
                    break;
                }
                if (cells[i][0] == "&")
                {
                    canNext = false;
                    GenerateOption(i);

                    break;
                }
                if (cells[i][0] == "end")
                {

                    EndChat();
                    break;
                }
            }
        }
    }
    public void EndChat()
    {
        print("EndChat" + textAsset.name);
        canNext = false;
        dialogIndex = 0;
        isBegin = false;

        if (chatPanel != null)
        {
            ClearAllPic();
            ClearAllChoices();
            //把所有Prefb消除
            ClearImageLiHui();
            ClearChatBoxText();
            chatPanel.transform.Find("Sprite").gameObject.SetActive(false);
            if (talkBox.activeSelf)
            {
                talkBox.gameObject.SetActive(false);
            }
        }
        //把当前path清空
        textAssetPath = null;
    }

选项与赋值

int likeValue;
    int energyValue;
    int[] time;
    //选择按钮
    public void OnOptionClick(int _id)
    {
        dialogIndex = _id;
        for (int i = 0; i < buttonGroup.childCount; i++)
        {
            Destroy(buttonGroup.GetChild(i).gameObject);
            canNext = true;
        }
        ShowDialogRow();

    }
    public void GenerateOption(int _index)//此处的——index是总的序列号
    {
        string[] cells = dialogRows[_index].Split(',');
        if (cells[0] == "&")
        {
            GameObject button = Instantiate(OptionalButton, buttonGroup);
            button.GetComponentInChildren<TMP_Text>().text = cells[3];
            button.GetComponent<Button>().onClick.AddListener(
                delegate
                {
                    if (cells[6] != "")
                    {
                        string[] judge = cells[6].Split('>');
                        string[] jump = cells[4].Split('&');
                        OptionJudge(judge[0], judge[1], int.Parse(jump[0]), int.Parse(jump[1]));
                    }
                    else if (cells[5] != "")
                    {
                        OptionEffect(cells[5]);
                        OnOptionClick(int.Parse(cells[4]));
                    }
                    else if (cells[5] == "")
                    {
                        OnOptionClick(int.Parse(cells[4]));
                    }
                }
                            );
            GenerateOption(_index + 1);
        }
    }

    /// <summary>
    /// 选项赋值效果
    /// </summary>
    /// <param name="_effect">哪个值改变</param>
    /// <param name="_param">值改变的大小</param>
    public void OptionEffect(string effect)
    {
        string[] effects = effect.Split('+');
        string _effect = effects[0];
        int _param = int.Parse(effects[1]);
        if (_effect == "好感度")
        {
            likeValue += _param;
            print("好感度" + _param);
        }
        if (_effect == "活力")
        {
            energyValue += _param;
            print("活力" + _param);
        }
    }

    /// <summary>
    /// 根据文件中的条件来判断应该跳转到哪;一般是>=这个条件的跳转到位置1,否则跳转到位置2
    /// </summary>
    /// <param name="_judge">判断的条件</param>
    /// <param name="_param2">条件的大小</param>
    /// <param name="_jump1">跳转的位置1</param>
    /// <param name="_jump2">跳转的位置2</param>
    void OptionJudge(string _judge, string _param2, int _jump1, int _jump2)
    {
        if (_judge == "好感度")
        {
            if (likeValue >= int.Parse(_param2))
            {
                OnOptionClick(_jump1);
            }
            else
            {
                OnOptionClick(_jump2);
            }

        }
        else if (_judge == "时间")
        {
            string[] timeJudge = _param2.Split(':');

            if (time[0] >= int.Parse(timeJudge[0]) && time[1] >= int.Parse(timeJudge[1]))
            {
                OnOptionClick(_jump1);
            }
            else
            {
                OnOptionClick(_jump2);
            }

        }
        else if (_judge == "活力")
        {
            if (energyValue >= int.Parse(_param2))
            {
                OnOptionClick(_jump1);
            }
            else
            {
                OnOptionClick(_jump2);
            }
        }
    }

/// <summary>
    /// 把所有选项都删除
    /// </summary>
    void ClearAllChoices()
    {
        Transform[] childs = buttonGroup.transform.GetComponentsInChildren<Transform>();
        //第0个是这个物体本身
        for (int i = 1; i < childs.Length; i++)
        {
            Destroy(childs[i].gameObject);
        }
    }

图片

图片读取(运用图集Atlas实现)

//所有图片,包括人物立绘与场景,都放在这个图集里
    SpriteAtlas _atals;
    string atlasResourcesPath = "Atalas/ChatAtalas";
    public Sprite LoadAtlasSprite(string _atalsname, string spriteName)
    {
        LoadAtalas();
        switch (_atalsname)
        {
            case "_atals":
                return LoadAtlasSprite(_atals, spriteName);
        }
        return null;
    }
    Sprite LoadAtlasSprite(SpriteAtlas _atals, string spriteName)
    {
        if (_atals == null)
        {
            Debug.Log(_atals.name + "_atals == null");
            return null;
        }
        if (_atals.GetSprite(spriteName) != null)
        {
            return _atals.GetSprite(spriteName);
        }
        Debug.Log("NotGetAtlasPic");
        return null;
    }

    void LoadAtalas()
    {
        if (_atals == null)
        {
            _atals = Resources.Load<SpriteAtlas>(atlasResourcesPath);
        }
    }

图片与对话系统接入

图片层级参考

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

立绘Prefb层级参考

Excel与Unity工作流(二):基础对话框架,excel,unity,游戏引擎,c#

[SerializeField] Image leftPic;
    [SerializeField] Image rightPic;

    [Header("全屏对话的人物Prefb")]
    [SerializeField] GameObject aPrefb;
    [SerializeField] GameObject bPrefb;

    /// <summary>
    /// 只针对全屏对话
    /// </summary>
    /// <param name="picName"></param>
    /// <param name="picturePos"></param>
    void UpdateImage(string picName, string picturePos)
    {
        LoadImageLiHui(picName, picturePos);
    }

    /// <summary>
    /// 根据图片名称给出准确的底图+表情
    /// </summary>
    void LoadImageLiHui(string combineName, string picturePos)
    {
        string[] s = combineName.Split("&");
        if (s.Length < 1) return;

        if (picturePos == "Clear")
        {
            //遍历Sprite的所有子物体,看哪个子物体里面有s[0]
            foreach (Transform child in transform.Find("Sprite").GetComponentInChildren<Transform>())
            {
                if (child.childCount > 0)
                {
                    //如果这个节点里有这个名字,就删掉这个子物体
                    if (child.GetChild(0).name.Contains(s[0]))
                    {
                        Destroy(child.GetChild(0).gameObject);
                    }
                }
            }
            return;
        }
        GameObject talkerObj = null;
        GameObject prefb = null;
        //print(combineName + picturePos);
        //print("s[0]" + s[0]);
        switch (s[0])
        {
            case "A":
                prefb = aPrefb;
                break;
            case "B":
                prefb = bPrefb;
                break;
        }
        if (prefb == null) return;
        //prefab先根据位置生成
        switch (picturePos)
        {
            //如果这个原本没有prefb,就生成新的,如果有,就删掉之前的,生成一个新的;如果是同名的prefb,就改表情和其他Adding
            //每个角色都应该有一个“默认”表情
            case "Left":
                talkerObj = ChangePosLiHui(leftPic.transform, prefb, s[0]);
                break;            
            case "Right":
                talkerObj = ChangePosLiHui(rightPic.transform, prefb, s[0]);
                break;            
        }
        //print("talkerObj.transform.parent.name" + talkerObj.transform.parent.name);
        //根据Excell里的拆分文字换不同图片
        //大部分角色有一个默认表情,如果没有表情,就用默认的
        if (s.Length == 1) return;
        //换表情
        if (LoadAtlasSprite("_atals", s[0] + "_" + s[1]) != null)
        {
            talkerObj.transform.Find("Emotion").GetComponent<Image>().sprite = LoadAtlasSprite("_atals", s[0] + "_" + s[1]);
        }
    }
    /// <summary>
    /// /改变制定位置的立绘
    /// </summary>
    GameObject ChangePosLiHui(Transform pos, GameObject prefb, string name)
    {
        GameObject talkerObj = null;
        if (pos.childCount == 0)
        {
            //print("pos.childCount == 0");
            talkerObj = Instantiate(prefb, pos);
        }
        else
        {
            if (pos.GetChild(0).name.Contains(prefb.name))
            {
                //print("pos.GetChild(0).name.Contains(prefb.name)");
                talkerObj = pos.GetChild(0).gameObject;
                LiHuiReturnToDefalt(talkerObj, name);
            }
            else
            {
                //print("pos.GetChild(0).name.Don't Contains(prefb.name)");
                Destroy(pos.transform.GetChild(0).gameObject);
                talkerObj = Instantiate(prefb, pos);
            }
        }
        return talkerObj;
    }

    /// <summary>
    /// 立绘返回默认状态
    /// </summary>
    void LiHuiReturnToDefalt(GameObject talkerObj, string name)
    {
        //print("LiHuiReturnToDefalt" + name);
        //换成默认表情
        if (LoadAtlasSprite("_atals", name + "_默认") == null)
        {
            return;
        }
        talkerObj.transform.Find("Emotion").GetComponent<Image>().sprite = LoadAtlasSprite("_atals", name + "_默认");
    }


    /// <summary>
    /// 把所有的图片全都换成透明(全屏对话)
    /// </summary>
    public void ClearAllPic()
    {
        leftPic.sprite = LoadAtlasSprite("_atals", "透明");
        rightPic.sprite = LoadAtlasSprite("_atals", "透明");
        backGroundPic.sprite = LoadAtlasSprite("_atals", "透明");
    }
    void ClearImageLiHui()
    {
        //遍历Sprite的所有子物体,看哪个子物体里面有s[0]
        foreach (Transform child in chatPanel.transform.Find("Sprite").GetComponentInChildren<Transform>())
        {
            if (child.childCount > 0)
            {
                Destroy(child.GetChild(0).gameObject);
            }
        }
    }

    
    public void UpdateBackGround(string picName)
    {
        if (picName == "Clear")
        {

            backGroundPic.sprite = null;
            //backGroundPic.sprite= backgroundDic["透明"];
            backGroundPic.sprite = LoadAtlasSprite("_atals", "透明");

        }
        else if (picName != "")
        {

            //backGroundPic.sprite = backgroundDic[picName];
            //print("picName" + picName);
            backGroundPic.sprite = LoadAtlasSprite("_atals", picName);

        }
    }

音频

[SerializeField] AudioSource backgroundSource;//背景音乐
    [SerializeField] AudioSource effectSource;//音效音乐
    /// <summary>
    /// 默认循环,开始播放就会切掉其他的音乐
    /// </summary>
    public void PlayBackGroundMusic(string musicName)
    {
        //print("play" + musicName);
        AudioClip audioClip = Resources.Load<AudioClip>("Audio/" + musicName);
        if (backgroundSource == null)//针对没有UI的测试Scene
        {
            print("backgroundSource == null");
            backgroundSource = GetComponent<AudioSource>();
        }
        if (audioClip == null) return;
        backgroundSource.clip = audioClip;
        backgroundSource.Play();
        backgroundSource.loop = true;
    }

    /// <summary>
    /// 播放音效 只播放一次
    /// </summary>
    public void PlayEffectMusic(string musicName)
    {
        AudioClip audioClip = Resources.Load<AudioClip>("Audio/" + musicName);
        if (audioClip == null)
        {
            //print(musicName + "audioClip == null");
            return;
        }
        //如果没有Canvas,或者Canvas里面没有EffectSource
        if (effectSource == null)
        {
            effectSource = GameObject.Find("Canvas").transform.Find("EffectSound").GetComponent<AudioSource>();
            if (effectSource == null)
            {
                //print("effectSource == null" + musicName);
                return;
            }
        }
        effectSource.clip = audioClip;
        effectSource.Play();
        effectSource.loop = false;
    }

文本

[SerializeField] GameObject nameObj;
    [SerializeField] TMP_Text nameText;
    /// <summary>
    /// 更新对话框
    /// </summary>
    /// <param name="_name"></param>
    /// <param name="_text"></param>
    void UpdateText(string _name, string _text)
    {
        if (_name == "")
        {
            print("_name == 空");
            nameObj.SetActive(false);
        }
        else
        {
            nameObj.SetActive(true);
        }
        nameText.text = _name;
        dialogText.text = _text;
    }

    public void ClearChatBoxText()
    {
        //清空内容
        dialogText.text = "";
        nameText.text = "";
    }

生命周期

void Start()
    {
        BeginChat();
    }

    // Update is called once per frame
    void Update()
    {
        //按空格键继续对话
        if ((Input.GetKeyDown(KeyCode.Space)||Input.GetMouseButtonDown(0) )&& canNext)
        {
            ShowDialogRow();
        }
    }

拓展

Excel与Unity工作流(三):对话框架拓展:Excel表内变量导入 赋值 判断-CSDN博客

Excel与Unity工作流(四):对话框架拓展:结合MVE实现Excel调用函数与批量支线导入管理思路-CSDN博客

参考:

https://www.bilibili.com/video/BV1v5411D79x/

到了这里,关于Excel与Unity工作流(二):基础对话框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【activiti】工作流入门基础概念

    为什么使用activiti 状态--------------------------------------------------引擎engin(业务变动不影响程序的进行) 每个人只能看到个人负责的,流程变更困难 bpmn建模语言 activiti流程步骤 步骤: 1、部署流程 2、定义流程 3、部署流程定义:使用activiti中的API把流程定义存储,在Acitivti执行过

    2024年02月16日
    浏览(39)
  • 生信工作流框架搭建 | 04-nextflow与Slurm高性能计算

    本篇为biodoge《生信工作流框架搭建》系列笔记的第5篇,该系列将持续更新。 上回生信工作流框架搭建 | 03-nextflow与AWS批量计算为大家提供了nextflow上云的实用教程。虽然AWS批量计算技术含量较大,但毕竟在国内应用场景较少,下面将为大家介绍另一种更为常用的应用:阿里云

    2024年02月15日
    浏览(100)
  • 【Unity】搭建Jenkins打包工作流,远程打热更、构建App

    Jenkins是团队协作项目打包常用的工作流,不多做介绍。 Jenkins的部署Unity打包环境还是非常简单的: 工作流程如下: 1. 在Jenkins中添加打包配置参数(如: 版本号, 目标平台等), 参数将以UI的形式显示在Jenkins Web界面以便打包前填写参数; 2. 用.bat批处理代码将步骤1的参数保存到

    2024年02月03日
    浏览(47)
  • Asp.net基于工作流引擎的系统框架设计开发(源代码+论文)

    工作流就是一系列相互衔接、自动进行的业务活动或任务。工作流引擎是工作流管理系统的核心,它的主要功能是通过计算机技术的支持去定义、执行和管理工作流,协调工作流执行过程中工作之间以及群体成员之间的信息交互。 论文主要讲述了工作流引擎的基本功能及设计

    2024年02月12日
    浏览(48)
  • Unity & PS Linear Workflow - Unity 和 PS 的线性工作流实践 - 简单配置示例

    因为 新的 Unity 项目人物走写实PBR风格 所以铁定基于 Linear Workflow 比基于 Gamma Workflow 的渲染效果更好 但是 Linear Workflow 下对 美术工作流不太友好,下面就实验并总结一些方案的优缺点 供大家选取 先看看不同 Color Space 下的 PBR 选择差异有多大 在 Unity Linear Color Space 渲染质量接

    2023年04月08日
    浏览(43)
  • Flutter 中的单元测试:从工作流基础到复杂场景

    对 Flutter 的兴趣空前高涨——而且早就应该出现了。 Google 的开源 SDK 与 Android、iOS、macOS、Web、Windows 和 Linux 兼容。单个 Flutter 代码库支持所有这些。单元测试有助于交付一致且可靠的 Flutter 应用程序,通过在组装之前先发制人地提高代码质量来确保不会出现错误、缺陷和缺

    2024年02月08日
    浏览(56)
  • 两小时快速入门 TypeScript 基础(一)工作流、基本类型、高级类型

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端(Node.js 等) 📃 个人状态: 2023届本科毕业生,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力 n 年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/

    2024年02月11日
    浏览(44)
  • 一个开源的汽修rbac后台管理系统项目,基于若依框架,实现了activiti工作流,附源码

    为了更加熟悉activiti工作流的使用和实战而改造的项目,欢迎大家参考和提出问题建议一起学习~ 源码gitee仓库地址:Yuzaki-NASA / Activiti7_test_car_rbac master分支是稳定版,dev分支是后来加了个新的并行审核流程和客户管理,个人测了多遍没啥问题,建议拉dev的代码。 sql文件在car

    2024年03月23日
    浏览(48)
  • Java spring boot 全解Camunda 7,从 0 到 1 构建工作流平台——第一节:各个开源框架对比

    引言:最近公司在做工作流这一块相关的东西,我是技术主要负责人之一。想着既然在公司做,用的是开源框架做的二开,反正也不涉及公司保密协议,也不涉及其它相关的法律问题,所以这里将自己做的那一部分公开出来,让后来者可以借鉴一二,或者给我斧正一二。 Ca

    2024年02月05日
    浏览(46)
  • 【工作流】Activiti工作流简介以及Spring Boot 集成 Activiti7

    什么是工作流? 工作流指通过计算机对业务流程进行自动化管理,实现多个参与者按照预定义的流程去自动执行业务流程。 文章源码托管:https://github.com/OUYANGSIHAI/Activiti-learninig Activiti5是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、

    2024年02月08日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包