news 2026/5/3 5:20:51

Unity 2021.3.21 实战:从零搭建一个带AI巡逻的第三人称射击Demo(上篇)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity 2021.3.21 实战:从零搭建一个带AI巡逻的第三人称射击Demo(上篇)

Unity 2021.3.21 实战:从零搭建带AI巡逻的第三人称射击Demo(上篇)

在独立游戏开发领域,快速验证核心玩法是项目成功的关键。本文将带你使用Unity 2021.3.21版本,从零开始构建一个具备基础AI功能的第三人称射击游戏原型。不同于简单的跟随教程,我们将深入探讨如何将游戏设计文档中的概念转化为可运行的代码实现,特别聚焦于AI巡逻与警戒系统的构建。

1. 项目初始化与环境配置

开始前,我们需要确保开发环境正确配置。Unity 2021.3.21作为长期支持版本(LTS),提供了稳定的开发基础。建议使用VS Code作为代码编辑器,通过以下步骤配置:

  1. 安装Unity 2021.3.21f1版本
  2. 在VS Code中安装C#扩展包
  3. 在Unity Editor中设置外部工具:
    • 菜单栏选择Edit > Preferences
    • 在External Tools选项卡中设置External Script Editor为VS Code
  4. 确保.NET版本与Unity兼容

提示:使用LTS版本可避免因引擎更新导致的兼容性问题,特别适合长期项目开发。

2. 游戏设计文档(GDD)核心要素

一个完整的GDD应包含以下关键部分,我们将其精简为射击游戏Demo所需的核心内容:

设计要素具体实现技术要点
核心概念躲避巡逻敌人并射击AI状态机、碰撞检测
移动机制WASD控制角色移动Rigidbody物理系统
视角控制鼠标控制第三人称相机摄像机跟随算法
战斗系统左键射击、生命值管理对象实例化与销毁
AI行为巡逻路线与警戒范围NavMesh与触发器

关键决策点:选择第三人称而非第一人称视角,可更好地展示角色动画与场景交互,同时降低摄像机控制复杂度。

3. 场景搭建与资源管理

3.1 基础场景构建

使用Unity原生几何体快速搭建游戏场景:

// 示例:通过代码创建基础地形 void CreateBaseTerrain() { GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane); ground.transform.localScale = new Vector3(5, 1, 5); ground.name = "Ground"; ground.tag = "Environment"; }

3.2 预制件系统实践

掩体等重复元素应使用预制件(Prefab):

  1. 创建基础立方体组合作为掩体原型
  2. 拖拽到Project视图创建预制件
  3. 通过代码实例化:
public GameObject coverPrefab; void SpawnCovers() { for(int i=0; i<4; i++) { Vector3 pos = new Vector3(i*10, 0, i*10); Instantiate(coverPrefab, pos, Quaternion.identity); } }

3.3 动态元素添加

为场景增加视觉反馈:

  1. 创建旋转的拾取物品:
    • 添加Animation组件
    • 设置Y轴旋转关键帧
  2. 粒子系统效果:
    • 使用Particle System组件
    • 调整发射器形状与生命周期

4. 角色控制系统实现

4.1 物理移动方案对比

我们对比两种移动实现方式:

方案优点缺点适用场景
Transform简单直接忽略物理交互简单原型
Rigidbody真实物理反馈实现复杂正式项目

推荐方案:采用Rigidbody物理移动,为后续碰撞检测和AI交互奠定基础。

4.2 完整角色控制器

[RequireComponent(typeof(Rigidbody))] public class PlayerController : MonoBehaviour { public float moveSpeed = 5f; public float rotateSpeed = 180f; public float jumpForce = 7f; public LayerMask groundLayer; private Rigidbody rb; private CapsuleCollider col; private bool isGrounded; void Start() { rb = GetComponent<Rigidbody>(); col = GetComponent<CapsuleCollider>(); } void Update() { HandleJumpInput(); } void FixedUpdate() { HandleMovement(); CheckGrounded(); } void HandleMovement() { float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); Vector3 movement = transform.forward * v * moveSpeed; Vector3 rotation = Vector3.up * h * rotateSpeed; rb.MovePosition(rb.position + movement * Time.fixedDeltaTime); rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation * Time.fixedDeltaTime)); } void HandleJumpInput() { if(Input.GetKeyDown(KeyCode.Space) && isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); } } void CheckGrounded() { float rayLength = col.bounds.extents.y + 0.1f; isGrounded = Physics.Raycast(transform.position, Vector3.down, rayLength, groundLayer); } }

4.3 摄像机跟随系统

第三人称摄像机需要解决的主要问题:

  1. 避免穿墙
  2. 平滑跟随
  3. 鼠标控制灵敏度
public class ThirdPersonCamera : MonoBehaviour { public Transform target; public Vector3 offset = new Vector3(0, 2, -3); public float smoothSpeed = 0.125f; public float mouseSensitivity = 2f; private float currentX = 0f; private float currentY = 0f; void LateUpdate() { currentX += Input.GetAxis("Mouse X") * mouseSensitivity; currentY -= Input.GetAxis("Mouse Y") * mouseSensitivity; currentY = Mathf.Clamp(currentY, -30, 70); Quaternion rotation = Quaternion.Euler(currentY, currentX, 0); Vector3 desiredPosition = target.position + rotation * offset; // 防止穿墙 RaycastHit hit; if(Physics.Linecast(target.position, desiredPosition, out hit)) { desiredPosition = hit.point - offset.normalized; } transform.position = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed); transform.LookAt(target); } }

5. 基础AI系统实现

5.1 巡逻状态设计

AI敌人需要具备三种基本状态:

  1. 巡逻状态:沿预定路径移动
  2. 警戒状态:发现玩家后追踪
  3. 攻击状态:进入攻击范围后行动
public enum AIState { Patrol, Alert, Attack }

5.2 巡逻路径系统

创建可复用的巡逻路径方案:

  1. 使用空对象作为路径点
  2. 通过脚本管理路径点序列
public class PatrolPath : MonoBehaviour { public List<Transform> waypoints = new List<Transform>(); public bool isCircular = true; void OnDrawGizmos() { if(waypoints.Count > 1) { for(int i=1; i<waypoints.Count; i++) { Gizmos.DrawLine(waypoints[i-1].position, waypoints[i].position); } if(isCircular) { Gizmos.DrawLine(waypoints[waypoints.Count-1].position, waypoints[0].position); } } } }

5.3 基础AI控制器

public class AIController : MonoBehaviour { public AIState currentState = AIState.Patrol; public PatrolPath patrolPath; public float moveSpeed = 3f; public float detectionRange = 8f; private int currentWaypoint = 0; private Transform player; void Start() { player = GameObject.FindGameObjectWithTag("Player").transform; } void Update() { switch(currentState) { case AIState.Patrol: PatrolBehavior(); CheckForPlayer(); break; case AIState.Alert: ChasePlayer(); break; } } void PatrolBehavior() { if(patrolPath.waypoints.Count == 0) return; Transform target = patrolPath.waypoints[currentWaypoint]; Vector3 direction = (target.position - transform.position).normalized; transform.position += direction * moveSpeed * Time.deltaTime; if(Vector3.Distance(transform.position, target.position) < 0.5f) { currentWaypoint = (currentWaypoint + 1) % patrolPath.waypoints.Count; } } void CheckForPlayer() { if(Vector3.Distance(transform.position, player.position) < detectionRange) { currentState = AIState.Alert; } } void ChasePlayer() { Vector3 direction = (player.position - transform.position).normalized; transform.position += direction * moveSpeed * Time.deltaTime; if(Vector3.Distance(transform.position, player.position) > detectionRange * 1.5f) { currentState = AIState.Patrol; } } }

5.4 视觉反馈增强

为AI状态变化添加视觉提示:

public class AIVisualFeedback : MonoBehaviour { public Material patrolMaterial; public Material alertMaterial; private Renderer renderer; private AIController aiController; void Start() { renderer = GetComponent<Renderer>(); aiController = GetComponent<AIController>(); } void Update() { renderer.material = aiController.currentState == AIState.Patrol ? patrolMaterial : alertMaterial; } }

6. 射击系统实现

6.1 子弹物理系统

创建子弹预制件需注意:

  1. 添加Rigidbody组件
  2. 设置适当的碰撞体
  3. 配置物理材质减少弹跳
public class Bullet : MonoBehaviour { public float speed = 50f; public float lifetime = 3f; public int damage = 10; void Start() { Destroy(gameObject, lifetime); } void FixedUpdate() { transform.position += transform.forward * speed * Time.fixedDeltaTime; } void OnCollisionEnter(Collision collision) { if(collision.gameObject.CompareTag("Enemy")) { collision.gameObject.GetComponent<AIHealth>().TakeDamage(damage); } Destroy(gameObject); } }

6.2 武器射击逻辑

public class Weapon : MonoBehaviour { public GameObject bulletPrefab; public Transform firePoint; public float fireRate = 0.2f; private float nextFireTime = 0f; void Update() { if(Input.GetMouseButton(0) && Time.time >= nextFireTime) { Shoot(); nextFireTime = Time.time + fireRate; } } void Shoot() { GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation); bullet.GetComponent<Rigidbody>().velocity = firePoint.forward * bulletPrefab.GetComponent<Bullet>().speed; } }

6.3 射击反馈优化

提升射击体验的关键细节:

  1. 添加枪口闪光粒子效果
  2. 实现后坐力动画
  3. 添加射击音效
public class WeaponEffects : MonoBehaviour { public ParticleSystem muzzleFlash; public AudioClip shootSound; void PlayShootEffects() { muzzleFlash.Play(); AudioSource.PlayClipAtPoint(shootSound, transform.position); } }

7. 碰撞检测与交互系统

7.1 触发器与碰撞器区别

特性碰撞器(Collider)触发器(Trigger)
物理反应
触发事件OnCollisionEnterOnTriggerEnter
性能消耗较高较低
典型用途物理交互检测区域

7.2 拾取物品实现

public class PickupItem : MonoBehaviour { public enum ItemType { Health, Ammo, Powerup } public ItemType type; public int value = 10; void OnTriggerEnter(Collider other) { if(other.CompareTag("Player")) { other.GetComponent<PlayerInventory>().AddItem(type, value); Destroy(gameObject); } } }

7.3 玩家库存系统

public class PlayerInventory : MonoBehaviour { public int health = 100; public int ammo = 30; public void AddItem(PickupItem.ItemType type, int value) { switch(type) { case PickupItem.ItemType.Health: health = Mathf.Min(health + value, 100); break; case PickupItem.ItemType.Ammo: ammo += value; break; } } }

在开发过程中,我发现AI巡逻系统最容易出现的问题是路径点索引越界。解决方案是使用模运算确保索引始终在有效范围内,如currentWaypoint = (currentWaypoint + 1) % waypoints.Count。另一个常见问题是角色控制器在斜坡上滑动,这需要通过调整物理材质或增加额外的地面检测射线来解决。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 5:16:41

AICoverGen:5分钟掌握AI翻唱神器,让任何声音唱出你的歌

AICoverGen&#xff1a;5分钟掌握AI翻唱神器&#xff0c;让任何声音唱出你的歌 【免费下载链接】AICoverGen A WebUI to create song covers with any RVC v2 trained AI voice from YouTube videos or audio files. 项目地址: https://gitcode.com/gh_mirrors/ai/AICoverGen…

作者头像 李华
网站建设 2026/5/3 5:16:08

基于Tauri+React构建本地AI桌面应用:跨平台打包与工程实践

1. 项目概述&#xff1a;一个本地的开源AI应用构建方案 最近在折腾一个挺有意思的桌面应用项目&#xff0c;叫 WhereClaw 。简单来说&#xff0c;它是一个基于 Tauri 框架构建的桌面应用&#xff0c;前端用 React &#xff0c;核心是捆绑了一个名为 whereclaw-engine …

作者头像 李华
网站建设 2026/5/3 5:03:22

告别手动录入!用Python的img2table库,5分钟把PDF/图片里的表格变成Excel

5分钟极速解放双手&#xff1a;用Pythonimg2table实现PDF/图片表格智能提取 市场部的小张每周都要花3小时手动录入20份扫描版行业报告里的数据表格。直到上个月&#xff0c;她发现用Python的img2table库处理同样工作只需12分钟——这不仅是效率的提升&#xff0c;更是工作方式…

作者头像 李华
网站建设 2026/5/3 5:01:47

轻量级任务编排工具Maestro:简化开发与运维自动化

1. 项目概述&#xff1a;一个面向开发者的轻量级任务编排与自动化工具 在软件开发与运维的日常工作中&#xff0c;我们常常会面对一系列重复、有依赖关系的任务。比如&#xff0c;一个典型的部署流程可能包括&#xff1a;拉取最新代码、运行单元测试、构建Docker镜像、推送镜像…

作者头像 李华