Unity开发技能树
🎯 完整技能图谱
1. 算法与数据结构 ⭐⭐⭐⭐⭐
// A. 寻路算法(游戏必备)
public class AStar {
public class Node {
public Vector2Int Position;
public float GCost; // 起点到当前点
public float HCost; // 当前点到终点(启发式)
public float FCost => GCost + HCost;
public Node Parent;
}
public List<Vector2Int> FindPath(Vector2Int start, Vector2Int end, bool[,] walkable) {
List<Node> openList = new List<Node>();
HashSet<Vector2Int> closedSet = new HashSet<Vector2Int>();
Node startNode = new Node { Position = start, GCost = 0, HCost = GetDistance(start, end) };
openList.Add(startNode);
while (openList.Count > 0) {
// 找F值最小的节点
Node current = GetLowestFCostNode(openList);
if (current.Position == end) {
return RetracePath(current);
}
openList.Remove(current);
closedSet.Add(current.Position);
// 检查相邻节点
foreach (var neighbor in GetNeighbors(current.Position, walkable)) {
if (closedSet.Contains(neighbor)) continue;
float newGCost = current.GCost + GetDistance(current.Position, neighbor);
Node neighborNode = openList.Find(n => n.Position == neighbor);
if (neighborNode == null) {
neighborNode = new Node {
Position = neighbor,
GCost = newGCost,
HCost = GetDistance(neighbor, end),
Parent = current
};
openList.Add(neighborNode);
} else if (newGCost < neighborNode.GCost) {
neighborNode.GCost = newGCost;
neighborNode.Parent = current;
}
}
}
return null; // 找不到路径
}
float GetDistance(Vector2Int a, Vector2Int b) {
int dx = Mathf.Abs(a.x - b.x);
int dy = Mathf.Abs(a.y - b.y);
// 对角线距离
if (dx > dy) {
return 14 * dy + 10 * (dx - dy);
}
return 14 * dx + 10 * (dy - dx);
}
Node GetLowestFCostNode(List<Node> nodes) {
Node lowest = nodes[0];
for (int i = 1; i < nodes.Count; i++) {
if (nodes[i].FCost < lowest.FCost) {
lowest = nodes[i];
}
}
return lowest;
}
List<Vector2Int> GetNeighbors(Vector2Int pos, bool[,] walkable) {
var neighbors = new List<Vector2Int>();
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0) continue;
Vector2Int neighbor = new Vector2Int(pos.x + x, pos.y + y);
if (IsWalkable(neighbor, walkable)) {
neighbors.Add(neighbor);
}
}
}
return neighbors;
}
bool IsWalkable(Vector2Int pos, bool[,] walkable) {
return pos.x >= 0 && pos.x < walkable.GetLength(0) &&
pos.y >= 0 && pos.y < walkable.GetLength(1) &&
walkable[pos.x, pos.y];
}
List<Vector2Int> RetracePath(Node endNode) {
List<Vector2Int> path = new List<Vector2Int>();
Node current = endNode;
while (current != null) {
path.Add(current.Position);
current = current.Parent;
}
path.Reverse();
return path;
}
}
// B. 空间分割(性能优化)
public class QuadTree<T> where T : class {
private const int MAX_OBJECTS = 4;
private const int MAX_LEVELS = 5;
private int level;
private List<T> objects;
private Rect bounds;
private QuadTree<T>[] nodes;
private System.Func<T, Vector2> getPosition;
public QuadTree(int level, Rect bounds, System.Func<T, Vector2> posGetter) {
this.level = level;
this.bounds = bounds;
this.getPosition = posGetter;
objects = new List<T>();
nodes = new QuadTree<T>[4];
}
public void Clear() {
objects.Clear();
for (int i = 0; i < nodes.Length; i++) {
if (nodes[i] != null) {
nodes[i].Clear();
nodes[i] = null;
}
}
}
private void Split() {
float subWidth = bounds.width / 2f;
float subHeight = bounds.height / 2f;
float x = bounds.x;
float y = bounds.y;
nodes[0] = new QuadTree<T>(level + 1, new Rect(x + subWidth, y, subWidth, subHeight), getPosition);
nodes[1] = new QuadTree<T>(level + 1, new Rect(x, y, subWidth, subHeight), getPosition);
nodes[2] = new QuadTree<T>(level + 1, new Rect(x, y + subHeight, subWidth, subHeight), getPosition);
nodes[3] = new QuadTree<T>(level + 1, new Rect(x + subWidth, y + subHeight, subWidth, subHeight), getPosition);
}
private int GetIndex(T obj) {
Vector2 pos = getPosition(obj);
int index = -1;
float verticalMidpoint = bounds.x + (bounds.width / 2f);
float horizontalMidpoint = bounds.y + (bounds.height / 2f);
bool topQuadrant = (pos.y < horizontalMidpoint);
bool bottomQuadrant = (pos.y > horizontalMidpoint);
if (pos.x < verticalMidpoint) {
if (topQuadrant) {
index = 1;
} else if (bottomQuadrant) {
index = 2;
}
} else if (pos.x > verticalMidpoint) {
if (topQuadrant) {
index = 0;
} else if (bottomQuadrant) {
index = 3;
}
}
return index;
}
public void Insert(T obj) {
if (nodes[0] != null) {
int index = GetIndex(obj);
if (index != -1) {
nodes[index].Insert(obj);
return;
}
}
objects.Add(obj);
if (objects.Count > MAX_OBJECTS && level < MAX_LEVELS) {
if (nodes[0] == null) {
Split();
}
int i = 0;
while (i < objects.Count) {
int index = GetIndex(objects[i]);
if (index != -1) {
nodes[index].Insert(objects[i]);
objects.RemoveAt(i);
} else {
i++;
}
}
}
}
public List<T> Retrieve(List<T> returnObjects, Rect area) {
int index = GetIndexForRect(area);
if (index != -1 && nodes[0] != null) {
nodes[index].Retrieve(returnObjects, area);
}
returnObjects.AddRange(objects);
return returnObjects;
}
private int GetIndexForRect(Rect area) {
int index = -1;
float verticalMidpoint = bounds.x + (bounds.width / 2f);
float horizontalMidpoint = bounds.y + (bounds.height / 2f);
bool topQuadrant = (area.y < horizontalMidpoint && area.y + area.height < horizontalMidpoint);
bool bottomQuadrant = (area.y > horizontalMidpoint);
if (area.x < verticalMidpoint && area.x + area.width < verticalMidpoint) {
if (topQuadrant) {
index = 1;
} else if (bottomQuadrant) {
index = 2;
}
} else if (area.x > verticalMidpoint) {
if (topQuadrant) {
index = 0;
} else if (bottomQuadrant) {
index = 3;
}
}
return index;
}
}
// C. 高级数据结构
// 优先队列(堆)
public class PriorityQueue<T> {
private List<(T item, float priority)> heap = new List<(T, float)>();
public int Count => heap.Count;
public void Enqueue(T item, float priority) {
heap.Add((item, priority));
int currentIndex = heap.Count - 1;
while (currentIndex > 0) {
int parentIndex = (currentIndex - 1) / 2;
if (heap[currentIndex].priority >= heap[parentIndex].priority) {
break;
}
var temp = heap[currentIndex];
heap[currentIndex] = heap[parentIndex];
heap[parentIndex] = temp;
currentIndex = parentIndex;
}
}
public T Dequeue() {
int lastIndex = heap.Count - 1;
var frontItem = heap[0];
heap[0] = heap[lastIndex];
heap.RemoveAt(lastIndex);
lastIndex--;
int currentIndex = 0;
while (true) {
int leftChildIndex = currentIndex * 2 + 1;
if (leftChildIndex > lastIndex) break;
int rightChildIndex = leftChildIndex + 1;
int smallerChildIndex = leftChildIndex;
if (rightChildIndex <= lastIndex && heap[rightChildIndex].priority < heap[leftChildIndex].priority) {
smallerChildIndex = rightChildIndex;
}
if (heap[currentIndex].priority <= heap[smallerChildIndex].priority) {
break;
}
var temp = heap[currentIndex];
heap[currentIndex] = heap[smallerChildIndex];
heap[smallerChildIndex] = temp;
currentIndex = smallerChildIndex;
}
return frontItem.item;
}
}
2. 性能优化(Profiling) ⭐⭐⭐⭐⭐
// A. CPU性能分析
using Unity.Profiling;
public class PerformanceMonitor : MonoBehaviour {
// 自定义性能标记
private static readonly ProfilerMarker updateMarker = new ProfilerMarker("GameLoop.Update");
private static readonly ProfilerMarker physicsMarker = new ProfilerMarker("GameLoop.Physics");
private static readonly ProfilerMarker aiMarker = new ProfilerMarker("GameLoop.AI");
void Update() {
updateMarker.Begin();
// 你的Update代码
UpdateGameLogic();
updateMarker.End();
}
void UpdateGameLogic() {
aiMarker.Begin();
UpdateAI();
aiMarker.End();
physicsMarker.Begin();
UpdatePhysics();
physicsMarker.End();
}
void UpdateAI() { }
void UpdatePhysics() { }
// 实时性能统计
void OnGUI() {
GUILayout.Label($"FPS: {1f / Time.deltaTime:F1}");
GUILayout.Label($"Draw Calls: {UnityEngine.Rendering.FrameDebuggerUtility.limit}");
GUILayout.Label($"Vertices: {GetVertexCount()}");
}
int GetVertexCount() {
int total = 0;
foreach (var renderer in FindObjectsOfType<MeshRenderer>()) {
var filter = renderer.GetComponent<MeshFilter>();
if (filter != null && filter.sharedMesh != null) {
total += filter.sharedMesh.vertexCount;
}
}
return total;
}
}
// B. 内存优化
public class MemoryOptimizer {
// 避免装箱
public void AvoidBoxing() {
// 错误:装箱
ArrayList list = new ArrayList();
list.Add(1); // int装箱为object
// 正确:泛型
List<int> genericList = new List<int>();
genericList.Add(1); // 无装箱
}
// 字符串优化
public class StringOptimization {
private static readonly StringBuilder sb = new StringBuilder(256);
// 错误:字符串拼接
public static string ConcatBad(string[] parts) {
string result = "";
foreach (var part in parts) {
result += part; // 每次创建新字符串
}
return result;
}
// 正确:StringBuilder
public static string ConcatGood(string[] parts) {
sb.Clear();
foreach (var part in parts) {
sb.Append(part);
}
return sb.ToString();
}
}
// LINQ优化
public void OptimizeLINQ() {
List<int> numbers = new List<int>();
// 错误:每次都分配新数组
var result1 = numbers.Where(n => n > 10).ToArray();
// 正确:重用数组或不ToArray
foreach (var n in numbers.Where(n => n > 10)) {
// 直接迭代,不分配数组
}
}
// 对象池避免GC
public class BulletPool {
private Queue<Bullet> pool = new Queue<Bullet>();
public Bullet Get() {
if (pool.Count > 0) {
return pool.Dequeue();
}
return new Bullet();
}
public void Return(Bullet bullet) {
bullet.Reset();
pool.Enqueue(bullet);
}
}
}
// C. GPU优化
public class GPUOptimization {
// 合批
public void Batching() {
// 1. Static Batching(静态合批)
GameObject[] staticObjects = new GameObject[100];
foreach (var obj in staticObjects) {
obj.isStatic = true;
}
// 2. Dynamic Batching(动态合批)
// 条件:相同材质、顶点<300、同一光照
// 3. GPU Instancing(GPU实例化)
Material mat = new Material(Shader.Find("Standard"));
mat.enableInstancing = true;
// 4. SRP Batcher(URP/HDRP)
// 材质使用兼容的Shader
}
// DrawCall优化
public void ReduceDrawCalls() {
// 合并Mesh
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];
for (int i = 0; i < meshFilters.Length; i++) {
combine[i].mesh = meshFilters[i].sharedMesh;
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
}
Mesh combinedMesh = new Mesh();
combinedMesh.CombineMeshes(combine);
GetComponent<MeshFilter>().mesh = combinedMesh;
}
}
3. 版本控制与协作 ⭐⭐⭐⭐⭐
# Git最佳实践
# .gitignore(Unity专用)
[Ll]ibrary/
[Tt]emp/
[Oo]bj/
[Bb]uild/
[Bb]uilds/
[Ll]ogs/
*.csproj
*.unityproj
*.sln
*.suo
*.user
# Git LFS(大文件存储)
*.psd filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
# 分支策略
# main - 稳定版本
# develop - 开发版本
# feature/xxx - 功能分支
# hotfix/xxx - 热修复分支
# 提交规范
git commit -m "feat: 添加玩家跳跃功能"
git commit -m "fix: 修复敌人AI卡死bug"
git commit -m "refactor: 重构背包系统"
git commit -m "perf: 优化渲染性能"
4. 测试(Testing) ⭐⭐⭐⭐
// A. Unity Test Framework
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using System.Collections;
public class PlayerTests {
// 单元测试(不依赖Unity)
[Test]
public void TestDamageCalculation() {
var player = new Player();
player.Health = 100;
player.TakeDamage(30);
Assert.AreEqual(70, player.Health);
}
// Unity测试(依赖Unity)
[UnityTest]
public IEnumerator TestPlayerJump() {
var go = new GameObject();
var player = go.AddComponent<PlayerController>();
player.Jump();
yield return new WaitForSeconds(0.5f);
Assert.Greater(player.transform.position.y, 0);
Object.Destroy(go);
}
// 性能测试
[Test, Performance]
public void TestPathfindingPerformance() {
var pathfinder = new AStar();
using (Measure.Scope()) {
pathfinder.FindPath(Vector2Int.zero, new Vector2Int(100, 100), null);
}
}
}
// B. 集成测试
public class IntegrationTests {
[UnityTest]
public IEnumerator TestBattleFlow() {
// 创建测试场景
var player = CreatePlayer();
var enemy = CreateEnemy();
// 模拟战斗
player.Attack(enemy);
yield return new WaitForSeconds(1f);
Assert.IsTrue(enemy.Health < 100);
// 清理
Object.Destroy(player.gameObject);
Object.Destroy(enemy.gameObject);
}
Player CreatePlayer() { return null; }
Enemy CreateEnemy() { return null; }
}
5. CI/CD(持续集成) ⭐⭐⭐⭐
# GitHub Actions示例
name: Unity Build
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: game-ci/unity-test-runner@v2
with:
unityVersion: 2021.3.0f1
- uses: game-ci/unity-builder@v2
with:
targetPlatform: Android
unityVersion: 2021.3.0f1
- uses: actions/upload-artifact@v2
with:
name: Build
path: build
6. 软技能与工程实践 ⭐⭐⭐⭐⭐
// A. 代码规范
public class CodingStandards {
// 命名规范
private int healthValue; // 私有字段:小驼峰
public int MaxHealth { get; set; } // 公共属性:大驼峰
public const int MAX_LEVEL = 100; // 常量:全大写
// 方法命名
public void CalculateDamage() { } // 动词开头
public bool IsAlive() { return true; } // bool返回用Is/Has/Can
// 注释规范
/// <summary>
/// 计算伤害值
/// </summary>
/// <param name="baseDamage">基础伤害</param>
/// <param name="multiplier">倍率</param>
/// <returns>最终伤害</returns>
public int CalculateFinalDamage(int baseDamage, float multiplier) {
return Mathf.RoundToInt(baseDamage * multiplier);
}
// TODO标记
// TODO: 实现伤害计算公式
// FIXME: 修复跳跃高度bug
// HACK: 临时解决方案,需要重构
// NOTE: 这里需要注意性能
}
// B. 错误处理
public class ErrorHandling {
public void LoadConfig(string path) {
try {
string json = System.IO.File.ReadAllText(path);
var config = JsonUtility.FromJson<GameConfig>(json);
}
catch (System.IO.FileNotFoundException) {
Debug.LogError($"配置文件不存在: {path}");
// 使用默认配置
}
catch (System.Exception e) {
Debug.LogException(e);
}
}
// 断言
public void ValidateInput(int level) {
UnityEngine.Assertions.Assert.IsTrue(level > 0, "等级必须大于0");
UnityEngine.Assertions.Assert.IsTrue(level <= 100, "等级不能超过100");
}
}
// C. 日志系统
public class Logger {
public enum LogLevel { Debug, Info, Warning, Error }
private static LogLevel minLevel = LogLevel.Info;
public static void Log(string message, LogLevel level = LogLevel.Info) {
if (level < minLevel) return;
string prefix = $"[{System.DateTime.Now:HH:mm:ss}] [{level}]";
switch (level) {
case LogLevel.Debug:
Debug.Log($"{prefix} {message}");
break;
case LogLevel.Warning:
Debug.LogWarning($"{prefix} {message}");
break;
case LogLevel.Error:
Debug.LogError($"{prefix} {message}");
break;
default:
Debug.Log($"{prefix} {message}");
break;
}
}
}
7. 项目管理工具 ⭐⭐⭐⭐
工具链必备:
├─ 版本控制: Git + GitHub/GitLab
├─ 项目管理: Jira / Trello / Notion
├─ 文档: Confluence / GitBook
├─ 代码审查: GitHub PR / GitLab MR
├─ 通信: Slack / Discord / 企业微信
├─ 资源管理: Plastic SCM / Perforce
└─ Bug追踪: Bugzilla / MantisBT
📊 完整技能树总结
🎮 Unity开发完整技能树
├─ 💻 编程基础(必须)
│ ├─ C# 高级特性
│ ├─ 面向对象编程
│ ├─ SOLID原则
│ └─ 设计模式
├─ 🎨 Unity核心
│ ├─ GameObject/Component
│ ├─ 物理系统
│ ├─ 动画系统
│ ├─ UI系统
│ ├─ 粒子系统
│ └─ Shader编程
├─ 🏗️ 架构与框架
│ ├─ MVC/MVVM
│ ├─ ECS架构
│ ├─ 事件驱动
│ └─ 依赖注入
├─ 🧮 算法与数据结构 ⭐
│ ├─ A*寻路
│ ├─ 空间分割(QuadTree/Octree)
│ ├─ 优先队列
│ └─ 图论算法
├─ ⚡ 性能优化 ⭐
│ ├─ Profiling分析
│ ├─ 内存管理
│ ├─ GPU优化
│ └─ 多线程
├─ 🔧 工具与工程化 ⭐
│ ├─ Git版本控制
│ ├─ CI/CD
│ ├─ 自动化测试
│ └─ 编辑器扩展
├─ 🌐 网络编程
│ ├─ Socket编程
│ ├─ 同步机制(帧同步/状态同步)
│ ├─ 网络优化
│ └─ 安全与防作弊
├─ 🎯 专项技能
│ ├─ Shader编程
│ ├─ 物理模拟
│ ├─ AI系统
│ ├─ 音频系统
│ └─ 动画系统
├─ 📱 平台适配
│ ├─ Android/iOS
│ ├─ PC/Console
│ ├─ WebGL
│ └─ VR/AR
└─ 💼 软技能 ⭐
├─ 代码规范
├─ 团队协作
├─ 文档撰写
├─ 技术选型
└─ 持续学习
💡 持续学习资源
- 技术博客:
– Unity官方博客
– 知乎Unity话题
– 优快云技术专栏 - 书籍推荐:
– 《游戏编程模式》
– 《Unity Shader入门精要》
– 《代码大全》
– 《重构:改善既有代码的设计》 - 视频教程:
– Siki学院
– 麦扣课堂
– Brackeys
– Unity Learn - 社区:
– Unity官方论坛
– GitHub
– Stack Overflow
– Discord服务器 - 实践项目:
– 参与开源项目
– 做个人项目
– 参加Game Jam
– 贡献技术文章
✨随便写写
by 瓜达卢佩·内特尔
568

被折叠的 条评论
为什么被折叠?



