《暗黑战神》个人部分学习笔记 记录
其他同学的笔记 csdn: 正版游戏的受害者 ARPG实战
音效播放服务中的一段代码
private Dictionary<string, AudioClip> adDic = new Dictionary<string, AudioClip>();
public AudioClip LoadAudio(string path, bool cache = false) {
AudioClip au = null;
if (!adDic.TryGetValue(path, out au)) {
au = Resources.Load<AudioClip>(path);
if (cache) {
adDic.Add(path, au);
}
}
return au;
}
讲一下逻辑和为什么这么设计
这段代码的逻辑是加载音频文件并进行缓存,以提高程序的运行效率。
具体来说,该函数接受一个字符串参数path表示要加载的音频文件的路径。如果cache为false,则每次调用该函数时都会重新加载音频文件;如果cache为true,则会先检查字典adDic中是否已经有了该路径对应的音频文件,如果已经存在,则直接从字典中获取;否则就通过Resources.Load方法加载该路径对应的音频文件,并将其添加到字典中以备下次使用。
为什么要设计成这样呢?因为Unity引擎在加载资源时需要消耗较长的时间和内存,反复加载同一资源会导致不必要的浪费。使用这种缓存机制可以避免重复加载同一资源,从而提高程序的性能表现。
Resources.Load方法加载资源是怎么回事
Resources.Load方法是Unity引擎提供的一种资源加载方式,可以用来加载工程中的资源文件。具体原理是Unity会将工程中所有资源预先打包成二进制格式,并存储在指定目录下的资源包(.asset)中。当我们使用Resources.Load方法时,Unity会根据传入的路径去寻找对应的资源包,并将其中的资源加载到内存中。
例如,我们可以通过以下代码来加载一个名为"Assets/Resources/TestImage.png" 的图片资源:
Texture2D texture = Resources.Load<Texture2D>("TestImage");
这里的参数"path"实际上表示相对于Resources目录下的路径。因此,上述代码中的"path"实际上是"TestImage"而不是 "Assets/Resources/TestImage.png"。
需要注意的是,由于Resources.Load方法是在运行时动态加载资源,因此它的性能可能较低。如果你需要加载的资源在游戏运行过程中不变且数量有限,建议使用更快速、更高效的资源管理方式,如将资源打包成AssetBundle或进行静态加载等。
unity中的InvokeRepeating用法
在Unity中,InvokeRepeating函数的作用是周期性地重复调用指定的方法。该函数可以帮助你在游戏中实现定期执行某个操作的需求。
下面是InvokeRepeating函数的使用方法:
InvokeRepeating("MethodName", time, repeatRate);
其中,MethodName是要重复调用的方法名,time是延迟调用的时间(单位为秒),repeatRate是每次调用方法之间的时间间隔(也是秒数)。
例如,如果想让某个函数每隔一段时间自动运行一次,可以使用以下代码:
using UnityEngine;
using System.Collections;
public class MyScript : MonoBehaviour {
void Start () {
InvokeRepeating("MyFunction", 2.0f, 0.5f);// 每隔0.5秒调用一次MyFunction()
}
void MyFunction () {
Debug.Log("MyFunction() is working!");
}
}
在该示例中,除了实现一个简单的输出日志的方法MyFunction外,还增加了Start函数,并在其中使用InvokeRepeating来调用MyFunction方法。该示例中,MyFunction将在启动2秒后开始被调用,并且将在每次调用之间间隔0.5秒。
需要注意的是,当不再需要重复调用函数时,应该使用CancelInvoke(string methodName)手动取消调用。
void StopMyFunction()
{
CancelInvoke("MyFunction");
}
这样就可以停止之前设置的并且正在运行的InvokeRepeating
设置Tips显示队列 中的部分代码和一些问题清理
C#的lock
在C#中,lock关键字通常用来保证线程安全。在多线程环境下,如果有两个或多个线程同时访问一个共享资源(如一个变量、一个文件、一段代码等),就可能会发生竞争条件(Race Condition)。在这种情况下,多个线程可能会同时读取和写入相同的数据,从而导致无法预料的结果。
通过使用lock,可以解决上述问题。在实现中,lock会将指定代码段标记为“临界区”,即只允许一个线程同时进入该区域,从而实现了对共享资源的互斥访问。
下面是lock关键字的语法:
lock (expression)
{
// 临界区
}
其中,expression通常为一个对象引用(也可以为值类型,但不建议),在语句块执行期间,如果该对象处于锁定状态,则阻塞线程并等待锁的释放;否则就将该对象锁定并执行语句块,当语句块执行完毕后,释放锁。
例如,以下示例演示了如何使用lock关键字实现线程安全的计数器:
public class Counter
{
private int count = 0;
public void Increment()
{
lock (this)
{
count++;
}
}
public int GetCount()
{
lock (this)
{
return count;
}
}
}
在这个示例中,Increment方法和GetCount方法都使用了lock关键字来保证线程安全。由于count变量是一个共享资源,我们需要确保任何时候只有一个线程能够修改它或读取它的值。通过在Increment和GetCount方法周围使用lock语句块,我们可以避免多个线程同时访问count变量,并实现线程安全。
需要注意的是,在使用lock关键字时,必须特别小心,避免死锁(Deadlock)等问题。也就是说,在代码块中不能嵌套使用多个lock,并且应尽可能减少锁定的范围,提高程序的并发性。
为什么AniPlayDone函数前面有IEnumerator
private void SetTips(string tips) {
SetActive(txtTips);
SetText(txtTips, tips);
AnimationClip clip = tipsAni.GetClip("TipsShowAni");
tipsAni.Play();
//延时关闭激活状态
StartCoroutine(AniPlayDone(clip.length, () => {
SetActive(txtTips, false);
isTipsShow = false;
}));
}
private IEnumerator AniPlayDone(float sec, Action cb) {
yield return new WaitForSeconds(sec);
if (cb != null) {
cb();
}
}
函数前面有IEnumerator关键字是因为该函数使用了yield return语句,这里的IEnumerator实际是一个迭代器。 迭代器是一种协同性质,它允许在某个方法内部控制枚举元素的方式。使用yield return语句时,编译器会自动将其转换成一个enumerator类型的对象,并返回一个IEnumerator接口用于控制此迭代过程。
在上述示例中,AniPlayDone函数定义为IEnumerator后,可以通过yield return new WaitForSeconds来等待指定时间间隔,此间隔作为迭代器的下一个值返回。当时间流逝到指定秒数之后,迭代器会继续执行,并调用cb回调函数。如果cb不为空,则执行回调函数,完成对延时操作的处理。
需要注意的是,当使用带有yield return的迭代器时,必须在使用它的代码中调用StartCoroutine()函数,以确保正常执行迭代逻辑。否则,程序将无法正确地执行yield return语句并更新UI等操作。
总之,通过将函数定义为IEnumerator类型,可以使函数功能更加灵活,而且能够支持异步的多线程协同处理。 异步处理的优势就是可以避免阻塞进程的同时优化程序响应速度和用户体验。
在Unity中,StartCoroutine是一个用于协程控制的函数。它可以启动一个协程,在后台执行某些代码,而不会阻塞主线程的运行。StartCoroutine接受不同的参数类型来支持不同的协程启动方式:
1、 IEnumerator对象:StartCoroutine可以接受一个返回IEnumerator类型的函数作为参数,该函数通常通过yield return语句分解成多个步骤,并提供适当的等待时间和逻辑操作实现异步处理。例如:
IEnumerator CoroutineFunction() {
yield return new WaitForSeconds(1.0f); // 等待1秒
Debug.Log("Coroutine is running.");
}
我们可以使用如下方法来启动上述协程:
StartCoroutine(CoroutineFunction());
2、 string参数:StartCoroutine还可以接受一个string类型的函数名字符串作为参数,该函数必须在MonoBehaviour类的派生类中实现,并返回IEnumerator类型。例如:
IEnumerator CoroutineFunction() {
yield return new WaitForSeconds(1.0f); // 等待1秒
Debug.Log("Coroutine is running.");
}
void Start() {
StartCoroutine("CoroutineFunction");
}
3、 Coroutine对象:StartCoroutine还可以接受一个返回Coroutine类型的函数,并返回一个Coroutine对象,用于对协程进行精确控制,例如暂停、恢复和停止协程。例如:
IEnumerator CoroutineFunction() {
yield return new WaitForSeconds(1.0f); // 等待1秒
Debug.Log("Coroutine is running.");
}
void Start() {
Coroutine coroutine = StartCoroutine(CoroutineFunction());
StartCoroutine(DelayCoroutine(coroutine));
}
IEnuerator DelayCoroutine(Coroutine coroutine) {
yield return new WaitForSeconds(3.0f);
StopCoroutine(coroutine);
}
在这个示例中,我们启动了一个协程,并拿到它的返回值Coroutine对象。然后,我们使用DelayCoroutine函数来延迟一定时间后停止该协程的执行,并通过StopCoroutine方法来终止协程。
总之,StartCoroutine是Unity的一个重要API,可以帮助开发人员实现异步处理,并有效提高游戏程序的性能和用户体验。
战斗逻辑框架介绍
MapMgr 地图管理器 如果有地图上的关卡触发器之类的