Unity的live2dgalgame多语言可配置剧情框架

这段代码用于读取表格

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OfficeOpenXml;
using System.IO;
using UnityEngine.Networking;
using UnityEngine.UI;
using Random = UnityEngine.Random;
 

public class Plots : MonoBehaviour
{
    public static string ReadingExcel;//正在读取的表格
    [Header("表格文件夹")] public static string URL =  Application.streamingAssetsPath;

    public static string PlotsEXCEL = "Plots";
    private static bool m_loaded;


    
    public  class plot
    {
        public string index, CN,NameCN, NameEN,EN,NameJP, JP,Face;
    }

    public static List<plot> S_Plots= new List<plot>();
    public virtual void OnEnable()
    {
        initialization();
    }

    //初始化
    public void initialization()
    {
        if (!m_loaded)
        {
            LoadExcel();
            m_loaded = true;
        } 

    }

    void LoadExcel()
    {

        //获取Excel文件的信息
        foreach (var VARIABLE in ReadFile())
        {
            Debug.Log("剧情挂载成功");
            FileInfo fileInfo = new FileInfo(VARIABLE);
            //加载背包信息
            if (VARIABLE.Contains(PlotsEXCEL))
            {

                //通过Excel表格的文件信息,打开Excel表格
                //使用using(){}语句命令,在结束时自动关闭文件
                using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
                {
                    //读取Excel中的第一张表, 注意EPPlus的索引值是从1开始的

                    ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
                    //取得第一行第一列的数据
//                    Debug.Log("行数"+worksheet.Dimension.End.Row + 1);

                    for (int Left = 2; Left < worksheet.Dimension.End.Row + 1; Left++) //根据行数遍历
                    {
                        
                            if (worksheet.Cells[Left, 1].Value.ToString().Length>0)
                            {
                                plot Plot = new plot();
                                Plot.index= worksheet.Cells[Left, 1].Value.ToString();
                                Plot.Face= worksheet.Cells[Left, 2].Value.ToString();
                                Plot.CN= worksheet.Cells[Left, 3].Value.ToString();
                                Plot.NameCN= worksheet.Cells[Left, 4].Value.ToString();
                                Plot.EN= worksheet.Cells[Left, 5].Value.ToString();
                                Plot.NameEN= worksheet.Cells[Left, 6].Value.ToString();
                                Plot.JP= worksheet.Cells[Left, 7].Value.ToString();
                                Plot.NameJP= worksheet.Cells[Left, 8].Value.ToString();
                                S_Plots.Add(Plot);
                            }

                        
               
                  
                    }
                }


            }
        }

    }

    /// <summary>
    /// 字符串转Enum
    /// </summary>
    /// <typeparam name="T">枚举</typeparam>
    /// <param name="str">字符串</param>
    /// <returns>转换的枚举</returns>
    public static T ToEnum<T>(string str)
    {
        try
        {
            return (T)Enum.Parse(typeof(T), str);
        }
        catch (ArgumentException e)
        {
            Debug.LogError("未找到系列"+str);
            throw;
        }

    }

 
    static List<string> ReadFile()
    {
        List<string> files = GetFiles(URL, "*.xlsx");


        List<string> GetFiles(string directory, string pattern)
        {
            List<string> files = new List<string>();
            foreach (var item in Directory.GetFiles(directory, pattern))
            {
                files.Add(item);
            }

            foreach (var item in Directory.GetDirectories(directory))
            {
                files.AddRange(GetFiles(item, pattern));
            }

            return files;
        }

        return files;
    }
}

这个方法里面设置剧情列表,和分支事件

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using DG.Tweening;
using Live2D.Cubism.Rendering;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using UnityEngine;
using UnityEngine.UI;

public class PlotReader : MonoBehaviour
{
   public Button[] ChoiceButton;
   public Text Text_Plot,Text_Name;
   public static int[] PlotBar;
   public static Action PlotFinEvent;
   public Button BTN_NextStep,BTN_Auto,BTN_Skip;
   public bool AutoMode,SkipMode;

   public Button LanguageCN, LanguageEN, LanguageJP;

   public float DomoveOffeset = 100;
   public float DomoveTime=0.1f;
   public GameObject Live2DFolider;

   public int index=0;
   private void Start()
   {
      LanguageCN.onClick.AddListener(()=>
      {
         Center.Language = "CN";
         RefreshLanguage();
      });
      LanguageEN.onClick.AddListener(()=>
      {
         Center.Language = "EN";
         RefreshLanguage();
      });
      LanguageJP.onClick.AddListener(()=>
      {
         Center.Language = "JP";
         RefreshLanguage();
      });
      BTN_Auto.onClick.AddListener(() =>
      {
         AutoMode = !AutoMode;
                  SkipMode = false;
               });
      BTN_Skip.onClick.AddListener(() =>
      {
         SkipMode = !SkipMode;
         AutoMode = false;
      });
      
      BTN_NextStep.onClick.AddListener(PlotNext);
      //注意!!! 剧情要-1,选项要-2
      SetPlotBar(new int[]{0,1,2,3,4,5,6,7},() => MakeChoice(new int[] { 7, 8 ,16}));
      PlotNext();
   }

   private float AutoTimeindex;
   private float AutoTimeMax = 3f;
   private void Update()
   {
      if (AutoMode)
      {
         if (index<=PlotBar.Length)
         {
            AutoTimeindex -= Time.deltaTime;
         }
         if (AutoTimeindex < 0 )
         {
            AutoTimeindex = AutoTimeMax;
            PlotNext();
         }
      }

      if (SkipMode)
      {
         if (index<=PlotBar.Length)
         {
            AutoTimeindex -= Time.deltaTime*20;
         }
         if (AutoTimeindex < 0 )
         {
            AutoTimeindex = AutoTimeMax;
            PlotNext();
         }
      }
   }

   public void PlotNext()
   {
      if (index<PlotBar.Length)
      {
         DoMethodEvent(Plots.S_Plots[PlotBar[index]].Face);
         Text_Plot.text = GetLanguagePlot(PlotBar[index]);
         Text_Name.text = GetLanguageName(PlotBar[index]);
      }else if (index== PlotBar.Length)
      {
         PlotFinEvent();
      }

      index++;
   }

   public void RefreshLanguage()
   {
      try
      {
         Text_Plot.text = GetLanguagePlot(PlotBar[index-1]);
         Text_Name.text = GetLanguageName(PlotBar[index-1]);
      }
      catch (IndexOutOfRangeException e)
      {
         Text_Plot.text = GetLanguagePlot(PlotBar[PlotBar.Length-1]);
         Text_Name.text = GetLanguageName(PlotBar[PlotBar.Length-1]);
      }
   
   }

   /// <summary>
   /// 根据语言获得剧情
   /// </summary>
   /// <param name="index"></param>
   /// <returns></returns>
   public string GetLanguagePlot(int index)
   {
    //  Debug.LogError(index+Plots.S_Plots[index].CN+Plots.S_Plots[index].NameCN);
      switch (Center.Language)
      {
         case "CN":
            return Plots.S_Plots[index].CN;
         case "EN":
            return Plots.S_Plots[index].EN;
         case "JP":
            return Plots.S_Plots[index].JP;
      }
Debug.LogError("语言??");
      return null;
   }
   /// <summary>
   /// 根据语言获得剧情
   /// </summary>
   /// <param name="index"></param>
   /// <returns></returns>
   public string GetLanguageName(int index)
   {
      switch (Center.Language)
      {
         case "CN":
            return Plots.S_Plots[index].NameCN;
         case "EN":
            return Plots.S_Plots[index].NameEN;
         case "JP":
            return Plots.S_Plots[index].NameJP;
      }
      Debug.LogError("语言??");
      return null;
   }
   
   
   /// <summary>
   /// 设置对话列表
   /// </summary>
   /// <param name="t"></param>
   public void SetPlotBar(int[] t,Action FinEvent)
   {
      PlotBar = t;
      PlotFinEvent = FinEvent;
   }

   #region 分支
   public void MakeChoice(int [] choices)
   {
      CloseAllButtons();
      for (int i = 0; i<choices.Length; i++)
      {
         int i1 = i;

         ChoiceButton[i1].gameObject.SetActive(true);
         ChoiceButton[i1].transform.Find("Text").GetComponent<Text>().text = GetLanguagePlot(choices[i1]+1);
         ChoiceButton[i1].onClick.AddListener(() =>
         {
            index = 0;
            Debug.LogError("执行事件BTN"+(choices[i1]+2));
            ExecuteMethodByName("BTN"+(choices[i1]+2));
            CloseAllButtons();
            PlotNext();
         });
      }
   }

   public void CloseAllButtons()
   {
      foreach (var VARIABLE in ChoiceButton)
      {
         VARIABLE.onClick.RemoveAllListeners();
         VARIABLE.gameObject.SetActive(false);
      }
   }

   public void BTN9()
   {
      SetPlotBar(new int[]{10,11,12,13},JumpToGameLevel);
   }
   public void BTN10()
   {
      SetPlotBar(new int[]{14,15,16},JumpToGameLevel);
   }
   public void BTN18()
   {
      SetPlotBar(new int[]{0,1,2,3,4,5,6,7},() => MakeChoice(new int[] { 7, 8 ,16}));
   }
   #endregion

   public void JumpToGameLevel()
   {
      Debug.LogError("将跳转场景");
   }
   public void ExecuteMethodByName(string methodName)
   {
      // 使用反射获取类的类型
      Type type = this.GetType();

      // 使用反射获取方法信息
      MethodInfo methodInfo = type.GetMethod(methodName);

      if (methodInfo != null)
      {
         // 调用匹配的方法
         methodInfo.Invoke(this, null);
      }
      else
      {
         Console.WriteLine("Method not found: " + methodName);
      }
   }

   public string[] CutMethod(string input)
   {
      return input.Split('+');
   }

   public void DoMethodEvent(string input)
   {
      Debug.LogError(input);
      foreach (var VARIABLE in CutMethod(input))
      {
         if (!VARIABLE.Contains("("))
         {
            ExecuteMethodByName(VARIABLE);
         }
         else if (VARIABLE.Contains("JOIN_"))
         {
            Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
            if (match.Success)
            {
               // 获取括号内的内容,并使用逗号分割
               string[] parts = match.Groups[1].Value.Split(',');

               if (parts.Length == 4)
               {
                  string stringValue = parts[0];
                  int intValue1, intValue2;
                  if (int.TryParse(parts[1], out intValue1) && int.TryParse(parts[2], out intValue2))
                  {
                     string stringValue2 = parts[3];

                     GameObject go = Instantiate(Resources.Load<GameObject>($"Live2D/{stringValue}"), Live2DFolider.transform);
                     go.transform.position = new Vector3(-999, -999, 2);
                     go.name = go.name.Replace("(Clone)", "");
                     Vector3 worldCoordinate= Vector3.down;
                     ;
                     switch (stringValue2)
                     {
                        case "Left":

                           worldCoordinate =Camera.main.ScreenToWorldPoint( new Vector3(intValue1-DomoveOffeset, intValue2, 2));
                           Debug.LogError(worldCoordinate);
                           worldCoordinate.z = 2;
                           go.transform.position = worldCoordinate;
                           worldCoordinate = Camera.main.ScreenToWorldPoint(new Vector3(intValue1, intValue2, 0));
                           worldCoordinate.z = 0;
                           go.transform.DOMove(worldCoordinate,DomoveTime);


                         
                           break;
                        case "Right":
                           worldCoordinate =Camera.main.ScreenToWorldPoint( new Vector3(intValue1+DomoveOffeset, intValue2, 2));
                           Debug.LogError(worldCoordinate);
                           worldCoordinate.z = 2;
                           go.transform.position = worldCoordinate;
                           worldCoordinate = Camera.main.ScreenToWorldPoint(new Vector3(intValue1, intValue2, 0));
                           worldCoordinate.z = 0;
                           go.transform.DOMove(worldCoordinate,DomoveTime);

                           break;
                     }

                  }
                  else
                  {
                     Debug.LogError("Failed to parse integers from the input.");
                  }
               }
               else
               {
                  Debug.LogError("Input does not contain 4 comma-separated values.");
               }
            }
            else
            {
               Debug.LogError("Input does not match the expected format.");
            }
         }
         else if (VARIABLE.Contains("SETA_"))
         {
            Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
            if (match.Success)
            {
               // 获取括号内的内容,并使用逗号分割
               string[] parts = match.Groups[1].Value.Split(',');

               if (parts.Length == 3)
               {
                  string stringValue = parts[0];
                  int intValue1, intValue2;
                  if (int.TryParse(parts[1], out intValue1) && int.TryParse(parts[2], out intValue2))
                  {
                     GameObject go = Live2DFolider.transform.Find(parts[0]).gameObject;
                     SetLive2DAlpha(go,intValue1,intValue2);
                  }
                  else
                  {
                     Debug.LogError("Failed to parse integers from the input.");
                  }
               }
               else
               {
                  Debug.LogError("Input does not contain 3 comma-separated values."+VARIABLE+$"length={parts.Length}");
               }
            }
            else
            {
               Debug.LogError("Input does not match the expected format.");
            }
         }
         else if (VARIABLE.Contains("FACE"))
         {
            Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
            if (match.Success)
            {
               // 获取括号内的内容,并使用逗号分割
               string[] parts = match.Groups[1].Value.Split(',');

               if (parts.Length == 2)
               {
                  string stringValue = parts[0];
                  string stringValue2 = parts[1];
                  GameObject go = Live2DFolider.transform.Find(parts[0]).gameObject;
                  SetFace(go,stringValue2);
               }
               else
               {
                  Debug.LogError("Input does not contain 4 comma-separated values.");
               }
            }
            else
            {
               Debug.LogError("Input does not match the expected format.");
            }
         }
         else if (VARIABLE.Contains("LEAV_"))
         {
            Match match = Regex.Match(VARIABLE, @"\(([^)]*)\)");
            if (match.Success)
            {
               // 获取括号内的内容,并使用逗号分割
               string[] parts = match.Groups[1].Value.Split(',');

               if (parts.Length == 2)
               {
                  string stringValue = parts[0];
                  GameObject go = Live2DFolider.transform.Find(parts[0]).gameObject;
                  Vector3 worldCoordinate;
                  StartCoroutine(des(go));
                        break;
                        IEnumerator des(GameObject go)
                        {
                           SetLive2DAlpha(go,
                              (int)(go.transform.Find("Drawables").GetComponentsInChildren<CubismRenderer>()[0]
                                 .GetComponent<CubismRenderer>().Color.a)*100, 0);
                           Debug.LogError($"Des{go.name}");
                           yield return new WaitForSeconds(0.3f);
                           Destroy(go);
                        }
                  
               }
               else
               {
                  Debug.LogError("Input does not contain 4 comma-separated values.");
               }
            }
            else
            {
               Debug.LogError("Input does not match the expected format.");
            }
         }
      }
   }

   public void SetLive2DAlpha(GameObject target, int begin,int end)
   {
      StartCoroutine(AlphaChangerCoroutine(target, begin / 100.0f, end / 100.0f));
   }

   private IEnumerator AlphaChangerCoroutine(GameObject target, float begin, float end)
   {
      foreach (var renderer in target.transform.Find("Drawables").GetComponentsInChildren<CubismRenderer>())
      {
         Color currentColor = renderer.Color;
         currentColor.a = begin;
         Debug.LogError(currentColor.a);
         renderer.Color = currentColor;
      }
      float duration = 0.1f;
      int numSteps = 10;
      float stepTime = duration / numSteps;

      for (int step = 0; step <= numSteps; step++)
      {
         float a = begin;
         if (end>begin)
         {
            a += (end - begin) / numSteps * step;
         }
         else
         {
            a -= (begin - end) / numSteps * step;
         }
         foreach (var renderer in target.transform.Find("Drawables").GetComponentsInChildren<CubismRenderer>())
         {
//            Debug.LogError(renderer.name);
            Color currentColor = renderer.Color;
            currentColor.a = a;
            Debug.LogError(currentColor.a);
            renderer.Color = currentColor;
         }
         yield return new WaitForSeconds(stepTime);
      }
   }
   public void SetFace(GameObject go, string Animatorname)
   {
      go.GetComponent<Animator>().Play(Animatorname);
   }
   
   
}

最终效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/106736.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

unocss和tailwindcss css原子引擎

第一种tailwindcss&#xff1a; tailwindcss官网 https://tailwindcss.com/docs/grid-column 基本介绍及优点分析 Tailwind CSS 中文文档 - 无需离开您的HTML&#xff0c;即可快速建立现代网站 PostCss 处理 Tailwind Css 基本流程 PostCSS - 是一个用 JavaScript 工具和插…

java基础 集合2

前9点&#xff0c;在另一篇作品中&#xff0c;可以从集合1开始观看 9.List遍历方式&#xff1a; 10.Arraylist底层原理&#xff1a; 11.Linklist底层原理&#xff1a; 1.LinkedList做队列和栈&#xff1a; package day01;import java.util.ArrayList; import java.util.I…

深度学习与计算机视觉(一)

文章目录 计算机视觉与图像处理的区别人工神经元感知机 - 分类任务Sigmoid神经元/对数几率回归对数损失/交叉熵损失函数梯度下降法- 极小化对数损失函数线性神经元/线性回归均方差损失函数-线性回归常用损失函数使用梯度下降法训练线性回归模型线性分类器多分类器的决策面 soft…

Hadoop、Hive安装

一、 工具 Linux系统&#xff1a;Centos&#xff0c;版本7.0及以上 JDK&#xff1a;jdk1.8 Hadoop&#xff1a;3.1.3 Hive&#xff1a;3.1.2 虚拟机&#xff1a;VMware mysql&#xff1a;5.7.11 工具下载地址: https://pan.baidu.com/s/1JYtUVf2aYl5–i7xO6LOAQ 提取码: xavd…

项目经验分享|openGauss 陈贤文:受益于开源,回馈于开源

开源之夏 项目经验分享 2023 #08 # 关于 openGauss 社区 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合华为在数据库领域多年的经验&#xff0c;结合企业级场景需求&#xff0c;持续构建竞争力特性。同时openGauss也是…

基于FPGA的图像PSNR质量评估计算实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 设置较大的干扰&#xff0c;PSNR15。 设置较小的干扰&#xff0c;PSNR25。 2.算法运行软件版本 matlab2022a vivado2019.2 3.部分核心程序 ti…

如何在Postman中使用静态HTTP

首先&#xff0c;打开 Postman 软件。在 Postman 的菜单栏中&#xff0c;点击 “Preferences”&#xff08;偏好设置&#xff09;。 亲身经验&#xff1a;我自己尝试了这个方法&#xff0c;发现它非常适用于需要使用HTTP的场景。 数据和引证&#xff1a;根据 Postman 官方文档…

皮卡丘RCE靶场通关攻略

皮卡丘RCE靶场通关攻略 文章目录 皮卡丘RCE靶场通关攻略RCE(remote command/code execute)概述远程系统命令执行启动环境漏洞练习第一关exec "ping"第二关 exec "eval" RCE(remote command/code execute)概述 RCE漏洞&#xff0c;可以让攻击者直接向后台服…

Maven第三章:IDEA集成与常见问题

Maven第三章:IDEA集成与常见问题 前言 本章内容重点:了解如何将Maven集成到IDE(如IntelliJ IDEA或Eclipse)中,以及使用过程中遇到的常见的问题、如何解决,如何避免等,可以大大提高开发效率。 IEAD导入Maven项目 File ->Open 选择上一章创建的Maven项目 my-app查看po…

Linux学习第24天:Linux 阻塞和非阻塞 IO 实验(一): 挂起

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 在正式开始今天的笔记之前谈一下工作中遇见的一个问题。 本篇笔记主要学习Linux 阻塞和非阻塞 IO 实验&#xff0c;主要包括阻塞和非阻塞简介、等待队列、轮询、…

威联通NAS进阶玩法之使用Docker搭建个人博客教程

Hello大家好&#xff0c;本篇教程主要教大家在威联通的NAS上搭建属于自己的个人博客网站&#xff0c;首先介绍一下我使用的机器&#xff0c;四盘位威联通TS-464C2&#xff0c;搭载四核四线程的N5095处理器&#xff0c;支持4K60帧的输出以及PCIE3.0,可玩性还是非常高的。废话不多…

24 行为型模式-访问者模式

1 访问者模式介绍 访问者模式在实际开发中使用的非常少,因为它比较难以实现并且应用该模式肯能会导致代码的可读性变差,可维护性变差,在没有特别必要的情况下,不建议使用访问者模式。 2 访问者模式原理 3 访问者模式实现 我们以超市购物为例,假设超市中的三类商品: 水果,糖…

本地websocket服务端暴露至公网访问【内网穿透】

本地websocket服务端暴露至公网访问【cpolar内网穿透】 文章目录 本地websocket服务端暴露至公网访问【cpolar内网穿透】1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功…

QGIS008:QGIS拓扑检查、修改及验证

摘要&#xff1a;本文介绍使用QGIS拓扑检查器和几何图形检查器检查图层的拓扑错误&#xff0c;修改拓扑错误&#xff0c;并对修改后的图层进行错误验证。 实验数据&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Vy2s-KYS-XJevqHNdavv9A?pwdf06o 提取码&#xff1a…

nginx配置反向代理和动静分离应用

一. Nginx配置反向代理和实现动静分离与虚拟主机流程图&#xff1a; 二 .Nginx配置反向代理和实现动静分离与虚拟主机实现详细配置和效果图 2.1 nginx 配置反向代理 #在nginx.conf配置server同级下配置 include tomcat.conf# vim tomcat.conf upstream api.z.mukewang.com{…

线程安全问题

线程安全 简单来说&#xff0c;在多个线程访问某个方法或者对象的时候&#xff0c;不管通过任何的方式调用以及线程如何去交替执行。在程序中不做任何同步干预操作的情况下&#xff0c;这个方法或者对象的执行/修改都能按照预期的结果来反馈&#xff0c;那么这个类就是线程安全…

基于XML的Web服务Java接口(JAX-WS)、Jakarta XML Web Services Eclipse 实现

简介 JAX-WS&#xff08;Java API for XML-Based Web Services&#xff09;&#xff0c;是创建web服务的Java编程接口&#xff0c;特别是SOAP服务。是Java XML编程接口之一&#xff0c;是Java SE 和Java EE 平台的一部分。 JAX-WS 2.0 规范是代替JAX-RPC 1.0的下一代Web服务AP…

前端知识储备

前端知识储备 一. 什么是前端? """ 任何与用户直接打交道的操作界面都可以称之为前端 比如:电脑界面 手机界面 平板界面什么是后端后端类似于幕后操作者(一堆让人头皮发麻的代码)不直接跟用户打交道 """ 二. 为什么学前端? ""&quo…

Vue进阶(幺陆玖)项目部署后IE报 SCRIPT1002:语法错误 解决方案探讨

文章目录 一、前言二、组件懒加载2.1 什么是懒加载2.2 如何实现懒加载 三、延伸阅读 软件程序唤醒3.1 protocolCheck 实现3.2 自定义实现 四、拓展阅读 一、前言 Vue项目改造升级后&#xff0c;原本本地热部署后IE可正常打开的项目出现页面白屏且控制台给出SCRIPT1002:语法错误…

服务器执行命令或脚本的两种方式

目录 1、通过Runtime 2、通过ProcessBuilder 在java开发中&#xff0c;有时候需要与操作系统的命令行交互&#xff0c;执行特定的CMD命令或脚本。 我们有如下两种方式来实现: Java的Runtime类和ProcessBuilder类都允许Java程序启动一个新的进程并执行特定的命令&#xff0…