ByteFisher AI 编程实战(十):AI辅助Unity开发(上)——脚本与逻辑

作为 Unity 开发者,你可能已经感受到 AI 编程的潜力。Unity 的 C# 脚本开发具有高度模式化的特点——MonoBehaviour 生命周期、组件通信、协程等——这正是 AI 擅长的领域。

一、Unity 脚本中的 AI 适用场景

场景 AI 能力 示例
组件脚本生成 根据描述生成完整脚本 “创建第三人称角色移动控制器”
游戏逻辑 实现特定行为模式 “实现追踪玩家的敌人 AI”
动画控制 Animator 参数和状态逻辑 “根据移动速度切换行走/奔跑动画”
物理交互 碰撞/触发逻辑 “玩家进入触发器打开门”
工具脚本 编辑器扩展和辅助 “创建批量重命名编辑器工具”

二、C# 脚本生成实战

2.1 玩家控制脚本

在 Cursor 中创建新脚本,使用 Cmd+K:

1
2
3
4
5
6
创建一个第三人称角色控制器:
- 使用 CharacterController 组件
- WASD 控制移动,空格跳跃
- 摄像机跟随,鼠标控制视角旋转
- 支持冲刺(按住 Shift)
- 移动和跳跃时播放对应的动画

AI 会生成结构完整的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using UnityEngine;

public class ThirdPersonController : MonoBehaviour
{
[Header("移动参数")]
[SerializeField] private float walkSpeed = 5f;
[SerializeField] private float sprintSpeed = 8f;
[SerializeField] private float jumpForce = 8f;
[SerializeField] private float gravity = -20f;

[Header("摄像机")]
[SerializeField] private Transform cameraTarget;
[SerializeField] private float mouseSensitivity = 2f;

private CharacterController controller;
private Animator animator;
private Vector3 velocity;
private float currentSpeed;

private void Awake()
{
controller = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
}

private void Update()
{
HandleMovement();
HandleJump();
HandleCamera();
UpdateAnimation();
}

private void HandleMovement()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
currentSpeed = Input.GetKey(KeyCode.LeftShift) ? sprintSpeed : walkSpeed;
controller.Move(move * currentSpeed * Time.deltaTime);

velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}

private void HandleJump()
{
if (Input.GetButtonDown("Jump") && controller.isGrounded)
velocity.y = Mathf.Sqrt(jumpForce * -2f * gravity);
}

private void HandleCamera()
{
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
transform.Rotate(Vector3.up * mouseX);
cameraTarget.localRotation *= Quaternion.Euler(-mouseY, 0, 0);
}

private void UpdateAnimation()
{
if (animator == null) return;
animator.SetFloat("Speed", currentSpeed / sprintSpeed);
animator.SetBool("IsGrounded", controller.isGrounded);
}
}

2.2 敌人 AI 脚本

1
2
3
4
5
6
创建一个简单的敌人 AI:
- 在指定路径上巡逻(Waypoint 系统)
- 检测到玩家后追击
- 超出最大追击距离后返回巡逻
- 使用状态机模式(Idle/Patrol/Chase)
- 可视化 Gizmos 显示检测范围

AI 生成的敌人 AI 脚本包含完整的状态机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using UnityEngine;
using System.Collections.Generic;

public enum EnemyState { Idle, Patrol, Chase }

public class SimpleEnemyAI : MonoBehaviour
{
[Header("巡逻设置")]
[SerializeField] private List<Transform> waypoints;
[SerializeField] private float patrolSpeed = 2f;
[SerializeField] private float waitTime = 2f;

[Header("追击设置")]
[SerializeField] private float chaseRange = 10f;
[SerializeField] private float chaseSpeed = 5f;
[SerializeField] private float maxChaseDistance = 20f;

private EnemyState currentState;
private int currentWaypointIndex;
private float waitTimer;
private Transform player;

private void Awake()
{
player = GameObject.FindWithTag("Player").transform;
ChangeState(EnemyState.Patrol);
}

private void Update()
{
switch (currentState)
{
case EnemyState.Patrol: UpdatePatrol(); break;
case EnemyState.Chase: UpdateChase(); break;
case EnemyState.Idle: UpdateIdle(); break;
}

// 检测玩家进入范围
float dist = Vector3.Distance(transform.position, player.position);
if (dist <= chaseRange && currentState != EnemyState.Chase)
ChangeState(EnemyState.Chase);
}

private void UpdatePatrol()
{
if (waypoints.Count == 0) return;
Transform target = waypoints[currentWaypointIndex];
float dist = Vector3.Distance(transform.position, target.position);

if (dist < 0.5f)
{
waitTimer += Time.deltaTime;
if (waitTimer >= waitTime)
{
currentWaypointIndex = (currentWaypointIndex + 1) % waypoints.Count;
waitTimer = 0;
}
}
else
{
transform.position = Vector3.MoveTowards(
transform.position, target.position, patrolSpeed * Time.deltaTime);
transform.LookAt(target);
}
}

private void UpdateChase()
{
float dist = Vector3.Distance(transform.position, player.position);
if (dist > maxChaseDistance)
{
ChangeState(EnemyState.Patrol);
return;
}
transform.position = Vector3.MoveTowards(
transform.position, player.position, chaseSpeed * Time.deltaTime);
transform.LookAt(player);
}

private void UpdateIdle() { /* 空闲等待 */ }

private void ChangeState(EnemyState newState)
{
currentState = newState;
waitTimer = 0;
}

private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, chaseRange);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, maxChaseDistance);
}
}

2.3 对象池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ObjectPool<T> where T : MonoBehaviour
{
private readonly Queue<T> pool = new();
private readonly T prefab;
private readonly Transform parent;

public ObjectPool(T prefab, int initialSize, Transform parent = null)
{
this.prefab = prefab;
this.parent = parent;
for (int i = 0; i < initialSize; i++)
{
T obj = CreateNew();
obj.gameObject.SetActive(false);
pool.Enqueue(obj);
}
}

public T Get()
{
T obj = pool.Count > 0 ? pool.Dequeue() : CreateNew();
obj.gameObject.SetActive(true);
return obj;
}

public void Return(T obj)
{
obj.gameObject.SetActive(false);
pool.Enqueue(obj);
}

private T CreateNew()
{
T obj = Object.Instantiate(prefab, parent);
obj.name = $"{prefab.name}_{pool.Count}";
return obj;
}
}

2.4 动画控制脚本

1
2
3
在 Cursor 中用 Cmd+K 创建动画控制器脚本:
"创建一个动画控制脚本,根据角色的移动速度切换
Idle/Walk/Run 动画状态,并根据移动方向调整朝向"

2.5 物理交互脚本

1
2
3
4
5
生成一个触发器脚本:
- 玩家进入 Trigger 区域时开门
- 离开时关门
- 门平滑旋转打开
- 添加可配置的开门速度和角度

AI 生成后只需拖拽到 GameObject 上,配置好引用即可运行。

三、AI 与 Unity 项目的最佳实践

3.1 .cursorrules Unity 专属配置

利用 .cursorrules 设定 C# 编码规范和架构模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
你是一名资深的 Unity C# 开发者。

编码规范:
- 私有字段使用 _camelCase
- 使用 [Header] 和 [Tooltip] 组织 Inspector
- 序列化字段使用 [SerializeField] 而非 public

架构原则:
- 优先使用 ScriptableObject 架构
- 使用依赖注入而非 GetComponent 查找
- 使用对象池管理频繁创建的对象
- 使用事件系统(UnityEvent/C# event)解耦组件

避免:
- GameObject.Find / FindObjectOfType(运行时查找)
- Update 中的 GC Alloc(字符串拼接、LINQ)
- 频繁的 Instantiate/Destroy(用对象池替代)

项目信息:
- Unity 2022.3 LTS
- Universal Render Pipeline
- Cinemachine + Input System 包

3.2 AI 生成后的检查清单

检查项 说明 自动/手动
组件引用 AI 生成的代码中 Awake/Start 的 GetComponent 是否正确 自动
事件绑定 Inspector 中需要手动拖拽绑定的引用 手动
参数调整 生成的默认参数(速度、范围)是否合理 手动
命名空间 是否缺少 using 语句 自动
性能 Update 中是否有不必要的操作 自动

3.3 常见 Prompt 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Unity 脚本类型 Prompt 模板:

1. 玩家控制:
"创建一个[第一/第三]人称控制器,使用[CharacterController/Rigidbody],
支持[WASD/摇杆]移动,[空格跳跃],[Shift 冲刺]"

2. 敌人 AI:
"创建一个敌人 AI,使用状态机模式,包含[巡逻/追击/攻击]状态,
使用[NavMeshAgent/Transform.MoveTowards]移动"

3. UI 管理:
"创建一个血量条 UI 脚本,使用 Slider 组件,
支持[平滑过渡/渐变颜色/闪烁警告]"

4. 工具脚本:
"创建一个编辑器工具,批量[重命名/查找/替换]场景中的 GameObject"

本章小结

  • Unity 的 C# 脚本生成是 AI 的高价值场景,模式化代码效率提升显著
  • 第三人称控制器、敌人 AI(带状态机)、对象池等常见功能可一键生成
  • AI 能生成完整的动画控制、物理交互、UI 管理等脚本
  • .cursorrules 能让 AI 遵循 Unity 编码规范和架构模式
  • AI 生成的代码需要检查:组件引用、事件绑定、参数调整、性能
  • 模板化的 Prompt 能大幅提升生成质量——把”需求描述”变成”填空式”指令

下一篇继续 Unity 专题,用 AI 构建一个完整的游戏项目。

ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
ByteFisher AI编程实战教程 系列
第 10/10 篇
分享: