系列文章目录
暂无
前言
为什么会有这篇文章?
答案是在学习的过程中,自己个人能力不足,有许多东西不明白(更直接的就是在后期的跟做游戏中出现了bug却不懂得怎么去修改),所以打算在跟学一遍,将里面不懂的、出现过的知识点进行记录与学习,以此来提升自身能力。
有兴趣的小伙伴可以跳转到麦扣的课程进行学习。
《迷失岛2》游戏框架
教程的第一集主要实现的是场景的切换,点触操作的益智解谜游戏,需要通在不同场景中寻找道具、线索等,因此场景的切换是整个游戏能够运行的核心。
一、代码分析以及一些前端操作
1
首先我们要在项目中创建多个scene,每个scene代表着游戏里的一个场景
值得注意的是,麦扣还在项目里创建了一个场景叫Persistent,将摄像机以及manager的类型放在场景中,Persistent想当于这个游戏的大脑。
然后将场景的素材拖入在场景中。
2.
可以看到,在场景中有着白色的箭头,我们需要创建一个代码挂在这个位置,当我们的鼠标进行点击后我们就可以切换场景。
在箭头的位置添加GameObject,在该物体中添加组件Box collider2D(碰撞器),通过Physics2D的方法来判断物体的检测碰撞,从而判断鼠标是否点击了该位置。
注意要勾选is trigger。
我们这里只是检测点击,不会造成物理运动,不需要刚体(大概)
trigger是什么?
Collider(碰撞器)与IsTrigger(触碰器)详解
同时挂载脚本teleport
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Teleport : MonoBehaviour
{
/**
* 首先要清楚两个场景变量,一个当前场景,一个要去的场景
*/
[SceneName] public string sceneFrom;
[SceneName] public string sceneToGo;
//触发一个点击事件,当点击发生时,调用TranstionManager里的Transition方法
public void TeleportToScene()
{
TranstionManager.Instance.Transition(sceneFrom, sceneToGo);
/**
*
在Unity中,Instance通常是指某个类的单例实例,它可以被其他类或脚本访问和操作。
例如,我们可以通过GameManager.Instance来访问游戏管理器的单例实例。
这种设计模式被称为单例模式,它保证在整个应用程序中,某个类只有一个实例存在,并且该实例可以被任何其他类访问。
在实现单例模式时,通常使用静态变量来存储单例实例,并使用静态方法来获取该实例
* */
}
}
在Persistent中创建新的空object,叫Transition Manager,挂载脚本TransitionManager。我们希望在鼠标点击了箭头位置的碰撞体之后,将该物体的场景变量(sceneFrom、sceneToGO)传给TransitionManager,然后实现转移
public class TranstionManager : Singleton<TranstionManager>
{
[SceneName] public string startScene;
private bool isFade;//判断是否切换场景
public CanvasGroup fadeCanvasGroup;
public float fadeDuration;//用于控制渐进渐出的速度
private bool canTransition;
public void Transition(string from,string to)//用于场景切换
{
if(!isFade&&canTransition)
//运用协程的方法卸载当前的场景,重新加载要去到的那个场景,并且将它设置为Additive
StartCoroutine((IEnumerator)TransitionToScene(from,to));
}
private IEnumerator TransitionToScene(string from,string to)
{
yield return Fade(1);
if(from!=string.Empty)
{
//在场景卸载前,订阅了该事件的方法得以执行
EventHandler.CallBeforeSceneUnloadEvent();
yield return SceneManager.UnloadScene(from);
//在协程中卸载指定的场景,并在卸载完成后暂停当前协程的执行,等待下一帧继续执行协程。
}
yield return SceneManager.LoadSceneAsync(to, LoadSceneMode.Additive);
//关于 LoadSceneMode.Additive 参数,它表示将指定的场景附加到当前场景中,
//而非替换当前场景(替换该场景可能带来的影响可能会是例如将H1的东西在H2中加载,如果再次返回H2时,H2原本的场景的东西被替换(自己的猜想,水平有限))。
//这意味着,我们可以同时加载多个场景,并在它们之间进行切换或合并等操作,
//从而实现更加复杂的场景管理和控制。
//设置新场景为激活场景
Scene newScene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);
SceneManager.SetActiveScene(newScene);
/**
* 最后,通过SceneManager.GetSceneAt方法获取最后一个添加到场景列表中的场景,即新加载的场景,
* 并通过SceneManager.SetActiveScene方法将其设置为激活场景,从而切换到新场景。
需要注意的是,由于该方法使用了yield return语句,因此在调用该方法时,实际上只会执行第一个yield return语句,
然后暂停方法的执行,直到迭代器被枚举时才会继续执行下一个yield return语句。
这种方式可以将复杂的场景切换操作分解成多个步骤,让每个步骤都可以异步执行,
并在必要时暂停执行,从而避免阻塞主线程。
*/
EventHandler.CallAfterSceneUnloadEvent();
yield return Fade(0);
}
private IEnumerator Fade(float targetAlpha)
//用于切换场景的渐入渐出
{
//传进一个数,如果是0就将canvas变成透明的,如果是1就变成黑色
isFade = true;
//核心方法:在场景中创建一个canvas
fadeCanvasGroup.blocksRaycasts = true;
float speed = Math.Abs(fadeCanvasGroup.alpha - targetAlpha)/fadeDuration;
while (!Mathf.Approximately(fadeCanvasGroup.alpha, targetAlpha))
{
fadeCanvasGroup.alpha = Mathf.MoveTowards(fadeCanvasGroup.alpha, targetAlpha,speed * Time.deltaTime);
yield return null;
}
fadeCanvasGroup.blocksRaycasts = false;
isFade = false;
}
}
单例模式的含义以及使用单例模式的优点(问AI的)
这段代码是一个Unity中的单例模式实现,其中Singleton是一个自定义的模板类,用于创建单例对象。该模板类有一个模板参数T,表示需要创建单例对象的类型。
在该代码中,模板参数T被指定为TransitionManager,因此该代码将创建一个TransitionManager类型的单例对象。
具体来说,该代码的作用是在Unity项目中创建一个唯一的TransitionManager对象,以便进行场景切换和过渡效果的管理。
Singleton类的实现方式是通过使构造函数为私有函数,并在类中保存一个静态实例来实现的。这样,每次需要访问该单例对象时,都可以调用GetInstance()静态函数来获取它的引用。如果该单例对象还未被创建,则会首先创建它并保存到静态实例中,然后返回它的引用。
因此,在这段代码中,TransitionManager类应该是一个用于管理场景切换和过渡效果的单例类。通过使用Singleton模板类,它被保证只创建一次,并且可以在整个Unity项目中方便地被访问和使用。
使用单例模式的主要好处包括以下几个方面:
确保只有一个实例:使用单例模式可以确保在应用程序中只存在一个实例。这在某些情况下是非常有用的,例如需要保证多个线程共享的对象在整个程序中只有一个实例。
方便全局访问:单例对象可以在整个应用程序中方便地被访问,因为它们只有一个实例,并且可以通过静态函数或者全局变量来获取它们的引用。
提高系统性能:在创建单例对象时,可以避免重复的对象创建和销毁操作,从而提高系统的性能。尤其是对于一些创建和销毁代价较大的对象,使用单例模式可以有效减少资源的浪费。
简化代码逻辑:使用单例模式可以简化代码的逻辑,因为只需要考虑如何创建和管理单例对象,无需关心对象的生命周期和状态,从而降低了代码的复杂度和维护成本。
总的来说,单例模式是一种非常有用的设计模式,它可以帮助开发人员解决许多常见的问题,并提高应用程序的性能和可维护性。
什么是协程?
Unity 协程(Coroutine)原理与用法详解 - 陳耿耿的文章 - 知乎
为什么在这里使用协程来切换场景?
在我看来是因为在同一帧里同时卸载一个场景与加载一个场景所耗费的性能过大,为了保障游戏运行的流畅,我们将这两个操作分开进行。
3.
在Persistent中创建一个新物体CursorManager,新建脚本Cursor Manager并添加
public class CursorManager : MonoBehaviour
{
private bool canclick;//判断是否可以点击
//首先要实现的是判断鼠标是否点击到了某一个东西。
//获取鼠标的世界坐标
private Vector3 mouseWorldPos => Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
//用update时刻检测是否有点击到碰撞体
private void Update()
{
canclick = ObjectAtMousePosition();
if (canclick && Input.GetMouseButtonDown(0))
//判断鼠标的位置是否有物体,并且鼠标按下了左键,这时候检测点击的是什么物体
{
//检测鼠标互动情况
ClickAction(ObjectAtMousePosition().gameObject);
}
}
private void ClickAction(GameObject clickObject)
{
switch(clickObject.tag)
//根据物体的标签判断这个物体是什么,并调用这个物体上的代码q
{
case "Teleport":
var teleport = clickObject.GetComponent<Teleport>();
teleport?.TeleportToScene();
break;
}
}
private Collider2D ObjectAtMousePosition()
{
return Physics2D.OverlapPoint(mouseWorldPos);
//检测鼠标范围内的碰撞体
}
}
总结
前前后后两天时间做了还是挺久的,但文章并不出彩,就当作是自己笔记吧!继续加油!