Unity格斗游戏期末大作业:从零搭建到打包的完整指南

发布时间:2026/9/30 14:08:27

Unity格斗游戏期末大作业:从零搭建到打包的完整指南 简介这是一份面向Unity初学者与在校学生的2D打怪物格斗游戏完整工程可直接作为期末大作业或课程设计参考。项目实现了角色上下左右跳跃移动、攻击怪物、血量条显示、分数统计与游戏背景切换等核心玩法并支持背景音乐调节和多个功能按钮适合用来练习Unity基础组件、碰撞检测与UI交互。压缩包为zip格式大小约50.78MB内含工程源码与相关资源文件解压后可直接导入Unity编辑器运行调试。目前已有2400人学习下载热度较高。通过这份资源读者可以拿到一套可直接运行的完整游戏案例理解角色控制、怪物AI、血量与计分系统的实现思路并在此基础上修改参数、替换素材快速完成自己的期末作品或入门练习。1. 从零搭一个能打的 Unity 格斗游戏期末大作业为什么选它最稳很多同学做 Unity 期末大作业时第一反应是做个打砖块或者见缝插针因为教程多、代码短。但如果你想让老师一眼记住同时又能真正练到 Unity 的核心机制打怪物格斗游戏其实是性价比最高的选择。它天然覆盖了状态机、碰撞检测、动画事件、UI 血条、音效反馈这几个必考知识点代码量可控美术资源用免费素材就能凑齐初学者花两三天也能跑出一个完整可玩的 Demo。这篇文章面向的是刚学 Unity 不久、需要交期末大作业、又不想只做换皮打砖块的初学者。我会按实际开发顺序从场景搭建、角色控制、攻击判定、怪物 AI、血条 UI 到打包提交把每一步的参数和踩坑点讲清楚。你跟着做最后手里会有一个能运行、能演示、代码结构清晰的格斗游戏项目而不是一堆散装脚本。2. 场景搭建与角色控制先把「能动能打」跑通2.1 项目初始设置与素材导入打开 Unity Hub 新建一个 2D 项目模板选 2D Core名字随便起比如MonsterFighter。创建完成后先别急着写代码把项目结构理清楚。在 Assets 下建几个文件夹Scripts、Sprites、Animations、Prefabs、Audio。这个习惯能让你后面找东西不抓狂老师翻你工程的时候也会觉得你靠谱。素材方面初学者不用自己画。Unity Asset Store 里搜2D fighter或者pixel monster筛选 Free 资源下载几个带 idle、walk、attack、hurt 动画的角色包。导入后把贴图拖进Sprites文件夹动画文件拖进Animations。注意导入贴图时选中图片在 Inspector 里把Texture Type确认为Sprite (2D and UI)Pixels Per Unit保持默认 100 就行。如果角色显示得太大或太小调这个值不要直接改 Transform 的 Scale不然后面物理和动画会乱。场景里建一个空物体叫Ground加BoxCollider2D用来做地面碰撞。再建一个Player空物体把角色 Sprite 拖进去作为子物体给 Player 加Rigidbody2D和CapsuleCollider2D。Rigidbody2D 的Gravity Scale设成 3 左右Freeze Rotation Z勾上防止角色被撞得旋转。Collider 的大小调到刚好包住角色身体不要包到武器武器判定后面单独做。2.2 用状态机思路写角色移动和跳跃初学者最容易犯的错是把移动、跳跃、攻击全塞在一个Update里用一堆 bool 互相判断最后自己都理不清。正确做法是用一个简单的枚举状态机。新建脚本PlayerController.cs挂在 Player 上。using UnityEngine; public class PlayerController : MonoBehaviour { public float moveSpeed 5f; public float jumpForce 12f; public Transform groundCheck; public LayerMask groundLayer; private Rigidbody2D rb; private Animator anim; private bool isGrounded; private float moveInput; // 状态枚举后续攻击状态也加在这里 private enum State { Idle, Run, Jump, Attack } private State currentState; void Start() { rb GetComponentRigidbody2D(); anim GetComponentInChildrenAnimator(); } void Update() { moveInput Input.GetAxisRaw(Horizontal); // 地面检测以 groundCheck 为圆心画小圆碰到 groundLayer 就算落地 isGrounded Physics2D.OverlapCircle(groundCheck.position, 0.1f, groundLayer); if (Input.GetButtonDown(Jump) isGrounded) { rb.velocity new Vector2(rb.velocity.x, jumpForce); } // 根据输入切换状态 if (isGrounded) currentState Mathf.Abs(moveInput) 0.01f ? State.Run : State.Idle; else currentState State.Jump; UpdateAnimation(); } void FixedUpdate() { rb.velocity new Vector2(moveInput * moveSpeed, rb.velocity.y); } void UpdateAnimation() { anim.SetBool(isRunning, currentState State.Run); anim.SetBool(isJumping, currentState State.Jump); // 翻转角色朝向 if (moveInput ! 0) transform.localScale new Vector3(Mathf.Sign(moveInput), 1, 1); } }这段代码的逻辑说明Update负责读输入和状态判断FixedUpdate负责物理移动两者分开是 Unity 的官方推荐做法。groundCheck是一个空物体放在角色脚底半径 0.1 的圆形检测足够判断是否落地。groundLayer在 Inspector 里设成 Ground 层记得给 Ground 物体指定 Layer。Mathf.Sign用来翻转角色朝向比手动写 if-else 简洁。参数方面moveSpeed设 5 比较跟手jumpForce设 12 能跳过一人高的平台。如果你觉得跳跃太飘把 Rigidbody2D 的Gravity Scale调到 4 或 5。动画状态机里建两个 Bool 参数isRunning和isJumping分别连到 Run 和 Jump 动画过渡条件记得取消Has Exit Time否则动画会卡顿。2.3 摄像机跟随与边界限制摄像机跟随是格斗游戏的标配。新建脚本CameraFollow.cs挂在 Main Camera 上。using UnityEngine; public class CameraFollow : MonoBehaviour { public Transform target; public float smoothSpeed 0.125f; public Vector2 offset new Vector2(0, 1); public float minX -10f; public float maxX 10f; void LateUpdate() { if (target null) return; Vector3 desiredPos new Vector3( Mathf.Clamp(target.position.x offset.x, minX, maxX), target.position.y offset.y, -10f ); transform.position Vector3.Lerp(transform.position, desiredPos, smoothSpeed); } }LateUpdate在角色移动之后执行避免摄像机抖动。smoothSpeed越小跟随越慢0.125 是常见值。minX和maxX限制摄像机左右范围防止跑出地图看到空白。offset的 y 设 1 让角色稍微偏下视野更舒服。注意摄像机 z 轴保持 -102D 游戏固定这个值。3. 攻击判定与怪物 AI让战斗真正打起来3.1 用动画事件触发攻击判定攻击判定不要用Input.GetKeyDown直接扣血那样会出现「按键就掉血」的假手感。正确做法是动画播放到出拳那一帧用动画事件调用判定函数。在 Attack 动画的最后一帧附近右键Add Animation Event函数名填OnAttackHit。using UnityEngine; public class PlayerAttack : MonoBehaviour { public Transform attackPoint; public float attackRange 0.5f; public LayerMask enemyLayer; public int attackDamage 1; private Animator anim; void Start() { anim GetComponentInChildrenAnimator(); } void Update() { if (Input.GetKeyDown(KeyCode.J)) { anim.SetTrigger(Attack); } } // 动画事件调用出拳瞬间执行 public void OnAttackHit() { Collider2D[] hitEnemies Physics2D.OverlapCircleAll( attackPoint.position, attackRange, enemyLayer); foreach (Collider2D enemy in hitEnemies) { enemy.GetComponentMonsterHealth()?.TakeDamage(attackDamage); } } // 在 Scene 视图画出攻击范围方便调试 void OnDrawGizmosSelected() { if (attackPoint null) return; Gizmos.color Color.red; Gizmos.DrawWireSphere(attackPoint.position, attackRange); } }attackPoint放在角色拳头位置attackRange设 0.5 左右enemyLayer指定 Monster 层。OnDrawGizmosSelected让你在 Scene 视图里看到红色圆圈调范围的时候不用瞎猜。?.是空引用保护防止怪物没有MonsterHealth组件时报错。动画状态机里加一个 Trigger 参数Attack从 Idle 和 Run 都能过渡到 Attack过渡时间设 0.1 秒取消Has Exit Time。3.2 怪物巡逻与追击的简易 AI怪物 AI 不需要太复杂初学者用「巡逻 发现玩家就追 进入攻击范围就打」三段式就够了。新建MonsterAI.cs。using UnityEngine; public class MonsterAI : MonoBehaviour { public float patrolSpeed 1.5f; public float chaseSpeed 3f; public float detectRange 5f; public float attackRange 1f; public Transform player; public Transform patrolPointA; public Transform patrolPointB; public int damage 1; private Rigidbody2D rb; private Animator anim; private Transform currentPatrolTarget; private float attackCooldown 1f; private float lastAttackTime; void Start() { rb GetComponentRigidbody2D(); anim GetComponentInChildrenAnimator(); currentPatrolTarget patrolPointB; } void Update() { float distanceToPlayer Vector2.Distance(transform.position, player.position); if (distanceToPlayer attackRange) { rb.velocity Vector2.zero; anim.SetBool(isMoving, false); TryAttack(); } else if (distanceToPlayer detectRange) { ChasePlayer(); } else { Patrol(); } } void Patrol() { anim.SetBool(isMoving, true); Vector2 dir (currentPatrolTarget.position - transform.position).normalized; rb.velocity new Vector2(dir.x * patrolSpeed, rb.velocity.y); if (Vector2.Distance(transform.position, currentPatrolTarget.position) 0.2f) { currentPatrolTarget currentPatrolTarget patrolPointA ? patrolPointB : patrolPointA; } FlipSprite(dir.x); } void ChasePlayer() { anim.SetBool(isMoving, true); Vector2 dir (player.position - transform.position).normalized; rb.velocity new Vector2(dir.x * chaseSpeed, rb.velocity.y); FlipSprite(dir.x); } void TryAttack() { if (Time.time - lastAttackTime attackCooldown) return; lastAttackTime Time.time; anim.SetTrigger(Attack); // 实际伤害在动画事件里调用 DealDamage } public void DealDamage() { if (Vector2.Distance(transform.position, player.position) attackRange) { player.GetComponentPlayerHealth()?.TakeDamage(damage); } } void FlipSprite(float dirX) { if (Mathf.Abs(dirX) 0.01f) transform.localScale new Vector3(Mathf.Sign(dirX), 1, 1); } }逻辑说明Update里按距离分三种情况优先级是攻击 追击 巡逻。attackCooldown防止怪物攻速过快1 秒是合理值。DealDamage同样挂在动画事件上保证伤害和动画同步。patrolPointA和patrolPointB是两个空物体放在场景里标记巡逻路线。detectRange设 5attackRange设 1这两个值根据你角色和怪物的大小微调。3.3 血条 UI 与受伤反馈血条用 Unity 自带的 Slider 就行。在 Canvas 下建两个 Slider一个叫PlayerHealthBar一个叫MonsterHealthBar。把 Slider 的Interactable取消勾选Transition设为 None。然后写Health.cs统一管理。using UnityEngine; using UnityEngine.UI; public class Health : MonoBehaviour { public int maxHealth 5; public Slider healthBar; public bool isPlayer; private int currentHealth; void Start() { currentHealth maxHealth; if (healthBar ! null) { healthBar.maxValue maxHealth; healthBar.value currentHealth; } } public void TakeDamage(int amount) { currentHealth - amount; currentHealth Mathf.Clamp(currentHealth, 0, maxHealth); if (healthBar ! null) healthBar.value currentHealth; // 受伤闪红 StartCoroutine(FlashRed()); if (currentHealth 0) Die(); } System.Collections.IEnumerator FlashRed() { SpriteRenderer sr GetComponentInChildrenSpriteRenderer(); if (sr null) yield break; Color original sr.color; sr.color Color.red; yield return new WaitForSeconds(0.1f); sr.color original; } void Die() { if (isPlayer) { UnityEngine.SceneManagement.SceneManager.LoadScene( UnityEngine.SceneManagement.SceneManager.GetActiveScene().name); } else { Destroy(gameObject); } } }maxHealth玩家设 5怪物设 3数值小一点战斗节奏快。FlashRed用协程做受伤反馈0.1 秒足够明显又不刺眼。玩家死亡直接重载当前场景怪物死亡销毁物体。注意healthBar在 Inspector 里拖对应的 Slider玩家和怪物各挂一个Health勾选isPlayer区分。4. 避坑与排查初学者最容易翻车的 5 个地方4.1 攻击判定打不中Gizmos 圆圈明明碰到了现象动画事件触发了OverlapCircleAll也执行了但怪物不掉血。原因九成是 Layer 没设对。enemyLayer在 Inspector 里要选 Monster 层同时怪物物体的 Layer 也要改成 Monster。如果怪物是子物体Layer 要设在挂 Collider 的那一层。解决选中怪物Inspector 顶部 Layer 下拉选 Monster如果没有就 Add Layer 新建。然后确认PlayerAttack脚本的enemyLayer字段选的是 Monster不是 Default。4.2 角色跳跃后卡在空中或者落地检测一直为 false现象按跳跃能跳但落地后isGrounded还是 false导致跳不起来。原因是groundCheck的位置不对或者groundLayer没设。解决在 Scene 视图里选中 Player看groundCheck空物体的位置是不是在脚底。如果角色 Collider 底部在 y0groundCheck的 y 设 -0.5 左右。groundLayer在 Inspector 里选 Ground 层同时 Ground 物体的 Layer 也要改成 Ground。检测半径 0.1 如果还不行调到 0.15。4.3 动画播放但角色不移动或者移动时动画不切换现象按方向键角色滑着走但 Idle 动画一直在播。原因是 Animator 参数名和代码里写的不一致或者过渡条件没设对。解决打开 Animator 窗口看 Parameters 里是不是有isRunning和isJumping拼写要和代码完全一致大小写敏感。然后点 Idle 到 Run 的过渡箭头Conditions 里加isRunning为 true取消Has Exit Time。Run 到 Idle 同理isRunning为 false。4.4 怪物攻击没有伤害或者伤害触发多次现象怪物挥拳动画播了但玩家不掉血或者一次攻击掉好几滴血。原因是动画事件没加或者加在了多个帧上。解决打开怪物 Attack 动画在出拳那一帧右键 Add Animation Event函数名填DealDamage。检查时间轴上是不是只有一个事件标记多了就删掉。另外确认MonsterAI脚本挂在怪物身上DealDamage是 public 方法。4.5 打包后 UI 血条不显示或者分辨率错乱现象在 Editor 里跑得好好的Build 出来血条不见了。原因是 Canvas 的Render Mode设成了Screen Space - Camera但没指定摄像机或者 UI 缩放没适配。解决选中 CanvasRender Mode改成Screen Space - Overlay这是最省事的。然后 Canvas Scaler 的UI Scale Mode选Scale With Screen SizeReference Resolution设 1920x1080Match设 0.5。这样不管什么分辨率血条都在该在的位置。5. 进阶技巧用对象池和音效让手感上一个台阶5.1 对象池管理怪物生成避免频繁 Instantiate如果你的游戏有刷怪逻辑直接Instantiate和Destroy会产生 GC 卡顿。初学者可以用一个简单的对象池。新建ObjectPool.cs。using System.Collections.Generic; using UnityEngine; public class ObjectPool : MonoBehaviour { public GameObject monsterPrefab; public int poolSize 10; private QueueGameObject pool new QueueGameObject(); void Start() { for (int i 0; i poolSize; i) { GameObject obj Instantiate(monsterPrefab); obj.SetActive(false); pool.Enqueue(obj); } } public GameObject GetMonster(Vector3 position) { if (pool.Count 0) { GameObject obj Instantiate(monsterPrefab); obj.SetActive(true); return obj; } GameObject monster pool.Dequeue(); monster.transform.position position; monster.SetActive(true); return monster; } public void ReturnMonster(GameObject monster) { monster.SetActive(false); pool.Enqueue(monster); } }poolSize设 10 够用怪物死亡时不Destroy而是调用ReturnMonster放回池子。这样运行久了也不会卡。注意怪物脚本里Die方法要改成调用对象池的ReturnMonster而不是Destroy(gameObject)。5.2 音效与打击感三个必须加的音效点格斗游戏的手感一半靠音效。至少加三个挥拳破空声、击中反馈声、受伤呻吟声。在PlayerAttack的OnAttackHit里击中敌人时播放hitSound在Health.TakeDamage里播放hurtSound。AudioSource 挂在角色上PlayOneShot播放短音效不要用Play否则会打断。音量Volume设 0.7 左右太高会爆。AudioClip 在 Asset Store 搜free fight sound就能找到。5.3 用宏定义区分 Editor 和打包环境热词里提到的unity宏定义在这里很有用。比如你想在 Editor 里显示 Gizmos 调试但打包后不显示可以用#if UNITY_EDITOR包住OnDrawGizmosSelected。再比如你想在 Editor 里按 R 键直接重开打包后禁用也可以加宏。这个技巧能让你的代码更专业老师看到会觉得你考虑周全。#if UNITY_EDITOR void Update() { if (Input.GetKeyDown(KeyCode.R)) { UnityEngine.SceneManagement.SceneManager.LoadScene( UnityEngine.SceneManagement.SceneManager.GetActiveScene().name); } } #endif最后说一个我自己的习惯每次改完代码先按 CtrlS 保存再回 Unity 等编译完成看到右下角没有红色报错再运行。这个习惯帮我省了无数次「改了没生效」的玄学时间。项目做完后把Scripts文件夹里的脚本按功能分好注释写清楚打包前在 Build Settings 里把场景加进去选 Windows 或 Mac 平台点 Build 就行。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/30 14:08:27

异或运算的底层原理与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 14:03:26

嵌入式驱动开发:从能跑到会崩的量产工程化鸿沟

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 14:03:26

MFC TCP网络通信实战:心跳保活、粘包处理与断线续传

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 14:48:41

零到全栈(无状态的 Web,怎么记住一个人)

上一篇完成了一次教科书式的两步走:先把存储代码从 main.py 原样搬进 storage.py,把 "取几条” 的决定权交还给调用方;再把存储实现整个换成 SQLite——建表、INSERT、一句 SELECT 加索引,接口约定纹丝不动,前端毫…

2026/9/30 14:48:41

月薪三万的Python开发者,每天都在用什么库

打开招聘网站,Python高级开发工程师的月薪普遍在2.5万到3万之间,AI应用方向甚至更高。高薪背后,不是会写更多语法,而是技术选型比别人更精准。月薪三万的Python开发者,每天都在用这些库。AI应用开发:LangCh…

2026/9/30 14:48:41

Telegram AI 翻译客服机器人源码搭建与避坑指南

简介:这是一套面向Telegram平台运营者与客服系统开发者的AI全自动翻译客服机器人源码,重点解决跨语言客户沟通中的实时翻译与本地化表达问题。机器人支持双向翻译,可将客户消息自动转换为客服预设语言,也能把客服回复翻译成符合客…

2026/9/30 14:48:41

数据结构与算法刷题全攻略:两遍刷题法真正掌握笔试算法

简介:面向备战大厂算法面试的求职者与在校生,这份压缩包是一份体系化的数据结构与算法刷题代码合集,覆盖剑指Offer题解、程序员代码面试指南、九章算法、牛客直通BAT课程及lintcode/大公司笔试真题编程题。资源同时收录第一遍学习代码和两个月…

2026/9/30 14:48:41

Android Studio 2021.2.1.10 Windows离线包部署与避坑指南

简介:Android Studio Chipmunk(2021.2.1)Beta 3 的 Windows 版安装包,面向需要在 Windows 平台搭建 Android 开发环境的移动开发者、学生与教学人员。作为 2021.2.1 分支的花栗鼠版本,它介于 Bumblebee 与 Dolphin 之间…

2026/9/30 14:43:40

大模型训练与推理优化(一)

大模型训练与推理优化 1. GPU 时间计算基础1.1 GPU 计算中的基本概念 h:hidden size,隐藏维度L:Transformer Block 层数V:词表大小参数量、FLOPs、显存和训练时间之间存在直接联系 1.2 Transformer 模型参数计算 1.2.1 Self-Atten…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 0:01:22

MATLAB+Yalmip+CPLEX实战:综合能源系统优化调度全流程解析

做综合能源系统优化调度这活儿,最痛苦的不是建模本身,而是模型写完之后不知道该怎么求解。看论文里轻飘飘一句“采用Yalmip调用CPLEX求解”,自己上手时却往往卡在环境配置、变量声明、约束写法和求解状态判读上,一耗就是两三天。这…

2026/9/30 0:01:22

I3C比I2C快10倍?RK3576实战:速率、DTS配置与混合总线避坑指南

I3C 比 I2C 快 10 倍?这句话在嵌入式群里传了很久,每次都能吵出一堆截图。前段时间我正好在 RK3576 上调板级 I3C 接口,从控制器寄存器一路摸到 Linux DTS 配置,踩了不少坑,也把这笔速度账彻底算明白了。本文就用 RK35…

2026/9/30 0:01:22

字符串转对象:JSON.parse、new Function与URLSearchParams

“字符串转对象”这几个字,我在技术群里见过的问法至少有十几种:有人拿着一串{a:1,b:2}说 JSON.parse 直接报错,有人要从 URL 里抠出参数,还有人只是想把abc变成能挂属性的东西。js 这门语言里,字符串和对象之间的转换…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/30 10:28:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑