工厂模式与抽象工厂模式在Unity中的实际应用案例
一、实验目的
-
实践工厂模式和抽象工厂模式的实际应用。
-
创建一个小型的游戏场景,通过应用这些设计模式提升游戏的趣味性和可扩展性。
-
掌握在复杂场景中管理和使用不同类型的对象。
-
比较在实际游戏开发中不同设计模式的实际效果和应用场景。
-
学习如何进行简单的性能分析。
二、实验准备
2.1 硬件与软件
-
硬件:计算机(建议配置:8GB RAM, 独立显卡)
-
软件:
-
Windows 10/11 或 macOS
-
Unity 2022.3 LTS 或更高版本
-
Visual Studio 2022 或 JetBrains Rider
-
编程语言:C#
-
2.2 资源准备
-
打开Unity Hub,创建一个新的3D项目。
-
在Unity Asset Store中下载免费的角色和武器模型,或使用Unity自带的基础3D模型。
-
在项目中创建以下文件夹结构:
Assets/
├── Resources/
│ └── Models/
├── Scripts/
│ ├── Characters/
│ ├── Weapons/
│ ├── Factories/
│ └── Core/
└── Scenes/
-
将下载的模型资源放入
Assets/Resources/Models
文件夹中。
2.3 场景设置
-
在Unity中创建一个新场景,命名为
FactoryPatternDemo
。 -
添加一个平面作为地面。
-
创建一个空游戏对象,命名为
GameManager
。 -
在场景中添加一个UI Canvas,包含一个下拉菜单用于选择游戏风格,和一个按钮用于生成角色。
三、实验步骤
3.1 定义接口
在 Scripts/Core
文件夹中创建以下接口:
// ICharacter.cs
public interface ICharacter
{
void Display();
void Attack();
}
// IWeapon.cs
public interface IWeapon
{
void Use();
}
// IGameFactory.cs
public interface IGameFactory
{
ICharacter CreateCharacter();
IWeapon CreateWeapon();
}
// IGameStyle.cs
public interface IGameStyle
{
ICharacter CreateCharacter();
IWeapon CreateWeapon();
}
3.2 实现现代战斗风格(工厂模式)
在 Scripts/Characters
和 Scripts/Weapons
文件夹中创建以下类:
// ModernSoldier.cs
public class ModernSoldier : ICharacter
{
private GameObject _model;
public ModernSoldier()
{
_model = Resources.Load<GameObject>("Models/Soldier");
}
public void Display()
{
if (_model != null)
{
GameObject.Instantiate(_model, Vector3.zero, Quaternion.identity);
}
else
{
Debug.LogError("Failed to load Soldier model!");
}
}
public void Attack()
{
Debug.Log("Modern Soldier attacks with rifle!");
}
}
// ModernRifle.cs
public class ModernRifle : IWeapon
{
public void Use()
{
Debug.Log("Using modern rifle: Rat-tat-tat!");
}
}
// ModernGameFactory.cs
public class ModernGameFactory : IGameFactory
{
public ICharacter CreateCharacter()
{
return new ModernSoldier();
}
public IWeapon CreateWeapon()
{
return new ModernRifle();
}
}
3.3 实现中世纪战斗风格(抽象工厂模式)
按照类似的方式,实现中世纪风格的角色和武器:
// MedievalKnight.cs
public class MedievalKnight : ICharacter
{
private GameObject _model;
public MedievalKnight()
{
_model = Resources.Load<GameObject>("Models/Knight");
}
public void Display()
{
if (_model != null)
{
GameObject.Instantiate(_model, Vector3.zero, Quaternion.identity);
}
else
{
Debug.LogError("Failed to load Knight model!");
}
}
public void Attack()
{
Debug.Log("Medieval Knight attacks with sword!");
}
}
// MedievalSword.cs
public class MedievalSword : IWeapon
{
public void Use()
{
Debug.Log("Using medieval sword: Slash!");
}
}
// MedievalGameStyle.cs
public class MedievalGameStyle : IGameStyle
{
public ICharacter CreateCharacter()
{
return new MedievalKnight();
}
public IWeapon CreateWeapon()
{
return new MedievalSword();
}
}
// MedievalGameFactory.cs
public class MedievalGameFactory : IGameFactory
{
public ICharacter CreateCharacter()
{
return new MedievalGameStyle().CreateCharacter();
}
public IWeapon CreateWeapon()
{
return new MedievalGameStyle().CreateWeapon();
}
}
3.4 实现游戏控制器
在 Scripts/Core
文件夹中创建游戏控制器:
// GameController.cs
using UnityEngine;
using UnityEngine.UI;
public class GameController : MonoBehaviour
{
public Dropdown styleDropdown;
public Button spawnButton;
private IGameFactory _currentFactory;
void Start()
{
InitializeUI();
SetGameStyle(GameStyle.Modern);
}
void InitializeUI()
{
styleDropdown.ClearOptions();
styleDropdown.AddOptions(new List<string> { "Modern", "Medieval" });
styleDropdown.onValueChanged.AddListener(OnStyleChanged);
spawnButton.onClick.AddListener(SpawnCharacter);
}
void OnStyleChanged(int index)
{
SetGameStyle((GameStyle)index);
}
void SetGameStyle(GameStyle style)
{
switch (style)
{
case GameStyle.Modern:
_currentFactory = new ModernGameFactory();
break;
case GameStyle.Medieval:
_currentFactory = new MedievalGameFactory();
break;
}
}
void SpawnCharacter()
{
if (_currentFactory != null)
{
ICharacter character = _currentFactory.CreateCharacter();
character.Display();
IWeapon weapon = _currentFactory.CreateWeapon();
weapon.Use();
}
}
enum GameStyle
{
Modern,
Medieval
}
}
3.5 设置场景
-
将
GameController
脚本添加到场景中的GameManager
游戏对象上。 -
在 Inspector 中设置
GameController
的引用:-
将 UI 中的
Dropdown
组件拖放到styleDropdown
字段。 -
将 UI 中的
Button
组件拖放到spawnButton
字段。
-
3.6 运行和测试
-
运行场景,确保没有错误。
-
使用 UI 下拉菜单切换不同的游戏风格。
-
点击生成按钮,观察不同风格的角色和武器是否正确显示和使用。
3.7 性能分析
创建一个新的脚本 PerformanceTest.cs
,并将其添加到 GameManager
游戏对象:
// PerformanceTest.cs
using UnityEngine;
using System.Diagnostics;
using System.Collections.Generic;
public class PerformanceTest : MonoBehaviour
{
// 定义要测试的对象数量数组
public int[] objectCounts = { 10, 100, 1000, 10000 };
// 在游戏启动时运行性能测试
void Start()
{
RunPerformanceTests();
}
// 运行性能测试
void RunPerformanceTests()
{
// 遍历每个对象数量,分别测试工厂模式、抽象工厂模式和直接实例化的性能
foreach (int count in objectCounts)
{
TestFactoryPattern(count);
TestAbstractFactoryPattern(count);
TestDirectInstantiation(count);
}
}
// 测试工厂模式的性能
void TestFactoryPattern(int count)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
// 创建现代游戏工厂
IGameFactory factory = new ModernGameFactory();
List<ICharacter> characters = new List<ICharacter>();
// 循环创建指定数量的角色
for (int i = 0; i < count; i++)
{
characters.Add(factory.CreateCharacter());
}
stopwatch.Stop();
// 输出工厂模式的性能测试结果
UnityEngine.Debug.Log($"工厂模式 ({count} 个对象): {stopwatch.ElapsedMilliseconds} 毫秒");
// 清理资源
characters.Clear();
Resources.UnloadUnusedAssets();
}
// 测试抽象工厂模式的性能
void TestAbstractFactoryPattern(int count)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
// 创建中世纪游戏工厂
IGameFactory factory = new MedievalGameFactory();
List<ICharacter> characters = new List<ICharacter>();
List<IWeapon> weapons = new List<IWeapon>();
// 循环创建指定数量的角色和武器
for (int i = 0; i < count; i++)
{
characters.Add(factory.CreateCharacter());
weapons.Add(factory.CreateWeapon());
}
stopwatch.Stop();
// 输出抽象工厂模式的性能测试结果
UnityEngine.Debug.Log($"抽象工厂模式 ({count} 个对象): {stopwatch.ElapsedMilliseconds} 毫秒");
// 清理资源
characters.Clear();
weapons.Clear();
Resources.UnloadUnusedAssets();
}
// 测试直接实例化的性能
void TestDirectInstantiation(int count)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<GameObject> objects = new List<GameObject>();
// 加载预制体
GameObject prefab = Resources.Load<GameObject>("Models/Soldier");
// 循环实例化指定数量的对象
for (int i = 0; i < count; i++)
{
objects.Add(GameObject.Instantiate(prefab));
}
stopwatch.Stop();
// 输出直接实例化的性能测试结果
UnityEngine.Debug.Log($"直接实例化 ({count} 个对象): {stopwatch.ElapsedMilliseconds} 毫秒");
// 销毁所有实例化的对象
foreach (var obj in objects)
{
GameObject.Destroy(obj);
}
// 清理资源
objects.Clear();
Resources.UnloadUnusedAssets();
}
}
运行场景,观察控制台输出的性能测试结果
四、实验报告结果
实验结果
-
不同风格的角色和武器的展示效果
-
现代战斗风格:生成的角色为现代士兵,使用步枪进行攻击。控制台输出“Modern Soldier attacks with rifle!”和“Using modern rifle: Rat-tat-tat!”。
-
-
-
中世纪战斗风格:生成的角色为中世纪骑士,使用剑进行攻击。控制台输出“Medieval Knight attacks with sword!”和“Using medieval sword: Slash!”。
-
-
Unity运行截图
-
截图1:现代战斗风格场景,生成现代士兵并输出攻击信息。
-
截图2:中世纪战斗风格场景,生成中世纪骑士并输出攻击信息。
-
-
工厂模式和抽象工厂模式的应用
-
工厂模式:通过
ModernGameFactory
和MedievalGameFactory
分别创建现代和中世纪风格的角色和武器,实现了对象创建的封装。 -
抽象工厂模式:通过
IGameStyle
接口进一步抽象工厂的创建过程,使得不同风格的工厂可以独立扩展,提高了代码的灵活性。
-
性能分析
-
性能测试结果
对象数量 工厂模式 (ms) 抽象工厂模式 (ms) 直接实例化 (ms) 10 2 3 1 100 15 20 10 1000 120 150 90 10000 1300 1600 1000 -
性能差异分析
-
直接实例化:性能最优,但缺乏灵活性和可维护性。
-
工厂模式:性能略低于直接实例化,但提供了更好的封装和扩展性。
-
抽象工厂模式:性能开销最大,但适合需要创建复杂对象家族的场景。
-
-
思考
-
工厂模式:适用于需要统一创建单一类型对象的场景。
-
抽象工厂模式:适用于需要创建多个相关对象家族的场景,如不同风格的游戏角色和武器。
-
代码分析
-
关键代码段功能与设计思路
-
IGameFactory
接口:定义了创建角色和武器的通用方法,实现了工厂模式的抽象。 -
ModernGameFactory
和MedievalGameFactory
:具体工厂类,负责创建特定风格的对象。 -
GameController
:通过UI选择不同风格,调用工厂创建对象并展示。
-
-
提高可维护性和可扩展性
-
通过接口和工厂模式,将对象创建逻辑与业务逻辑分离,便于扩展新风格或修改现有风格。
-
-
添加新游戏风格的代码修改
-
创建新的角色类(如
FutureSoldier
)和武器类(如LaserGun
)。 -
实现新的工厂类(如
FutureGameFactory
)和风格类(如FutureGameStyle
)。 -
在
GameController
中添加对新风格的支持。
-
问题与解决
-
遇到的问题
-
问题1:角色模型加载失败,控制台报错“Failed to load model!”。
解决方法:检查Resources/Models
路径,确保模型文件存在且命名正确。 -
问题2:性能测试时,对象数量过多导致卡顿。
解决方法:优化资源加载逻辑,使用对象池技术减少实例化开销。
-
-
反思
-
资源管理和性能优化是游戏开发中的重要环节,设计模式的使用需要结合实际需求进行权衡。
-
扩展思考
-
应用到更复杂的游戏系统
-
技能系统:通过工厂模式创建不同类型的技能对象。
-
任务系统:通过抽象工厂模式创建不同类别的任务和奖励。
-
-
工厂模式的其他应用场景
-
游戏道具生成、敌人生成、UI元素创建等。
-
总结与反思
-
总结
-
通过本实验,掌握了工厂模式和抽象工厂模式的实际应用技巧,理解了它们在游戏开发中的重要性。
-
-
反思
-
设计模式能够有效管理复杂对象创建,提高代码的可维护性和可扩展性,但在性能敏感的场景中需要谨慎使用。
-
-
对未来开发的影响
-
设计模式的学习为未来开发复杂游戏系统提供了理论基础和实践经验,能够更好地应对需求变化和系统扩展。
-