零、前言

最近无意中发现了《Unbound: Worlds Apart》游离于世界之海这款游戏,尤其欣赏其中魔术门双重世界的效果,正当作者想尝试实现下时,奈何在网上找了很久都没有找到对应效果的实现教程(可能是作者找得不够仔细),无奈之下作者只好自己摸索尝试。

一、简单介绍游戏及效果

《Unbound: Worlds Apart》是一款类《奥日》风格的2D平台冒险独立游戏。游戏中,玩家作为一名魔术师,可以通过召唤魔术门从而实现在不同的现实之间穿梭。每一道魔术门,可以体验到一个个不同的世界,而每个世界都有其独特的运行法则,比如反重力、时间控制,以及超级力量等等。


二、简单分析如何实现魔术门双重世界效果

2.1操作

玩家在游玩过程中,可以通过按键在当前位置生成一道魔术门。

2.2魔术门内外世界

魔术门的内外世界是通过SpriteMask遮罩实现的。场景中存在两重的世界,当玩家按下按键后,在玩家当前位置生成或激活一个指定半径大小的SpriteMask遮罩,在遮罩外部展示的是第一重世界的场景及物体,也就是不使用魔术门下玩家能看见的场景,而在遮罩内部展示的是第二重世界的场景及物体。


三、准备工作

3.1新建2D项目

3.2导入素材包

素材包地址

2D Game Kit | Tutorials | Unity Asset Store

3.3项目目录

3.3.1项目目录

四、正式开始

本次教程使用的是示例工程中的场景三。

4.1场景预览

打开场景三

4.2制作双重世界

由于我们具有一前一后的两个世界,所以需要用到两层场景。

4.2.1世界一

在场景根目录下新建父物体命名为World1,并将LevelAsssets下所有东西放在World1下作为World1的子物体。

4.2.2世界二

拷贝World1父物体并重命名为World2。

如果我们修改World1及World2的z轴坐标,那么二者在场景中的关系应该是如下的。

至此,我们双重世界的基础工作就完成了。

注意:我们无需更改World1与World2的坐标,上述更改只是为了更好演示两个世界逻辑关系,实际中前后关系是通过遮罩实现的。

4.3修改双重世界

我们修改场景三中间移动平台+毒液部分作为示范。我们希望的效果是:当玩家经过此处时,如果开启魔术门,则底下的毒液消失并变为平底,供玩家通过。

4.3.1修改世界一

所以说世界一应当是能被遮罩所遮挡的。为了更直观看见效果,我们先把World2禁用了。由于该项目中的需要被遮罩作用的毒液是采用MeshRenderer渲染的,所以步骤稍微麻烦一点,需要修改shader才能使spriteMask遮罩起作用,如果是一般的sprite渲染的话则相对简单很多。

4.3.1.1找到所需修改的Shader

找到World1中第二个Acid,也就是较长的毒液物体。

找到Acid MeshRenderer中的Water Material,并拷贝两份命名为Water_World1以及Water_World2。

找到Water身上Water2D shader,资源文件夹内搜索Water,找到后如下拷贝两份并命名为Water_World1及Water_World2。

4.3.1.2修改shader

打开编辑Water_World1 shader,主要修改内容如下。

4.3.1.3应用shader

找到Water_World1 Material,将Water_World1 shader应用到该Material上。

4.3.1.4修改Acid毒液

回到场景,关闭Acid身上SpriteMask组件防止干扰,并将上述修改的Water_World1 Material应用在Acid的MeshRenderer上。

4.3.2修改Player

4.3.2.1为Player添加SpriteMask子物体

4.3.2.2修改SpriteMask

遮罩的大小主要是通过SpriteMask的scale的控制。主要需要注意的点已圈出。

4.3.3修改世界一总结

如上修改后,已可实现如下效果。

4.3.4修改世界二

打开World2父物体,由于在World2中玩家应该是可以安全通过下面的毒液的,所以我们讲World2中相同位置上的Acid取消激活。

同理所有我们希望在World2中不显示或者显示的物体,都是通过以上思路实现的

再举例如下:World1与World2是相对的,若希望在World2的某位置生成一个土块,便可在World2下新增物体土块,而World1不新增即可。

4.4编码

由于魔法门是玩家控制开启与关闭的,我们不希望魔法门如刚所示一直呈打开状态。

4.4.1为Player添加脚本MaskController

 using System.Collections;using System.Collections.Generic;using UnityEngine;using Gamekit2D;​public class MaskController : MonoBehaviour{public GameObject world1; // 世界1父物体public GameObject world2; // 世界2父物体public SpriteMask mask; // 遮罩SpriteRenderer maskSP; // 遮罩spritepublic float maskRadius;// 遮罩半径[Tooltip("需要被遮罩作用的层")] public LayerMask maskLayers;[HideInInspector] public List<GameObject> setFalseGameObject; // world1中开启遮罩后取消的所有物体列表[HideInInspector] public List<GameObject> world1GameObjectList; // world1中所有物体列表​private void Start(){maskSP = mask.GetComponent<SpriteRenderer>();world1GameObjectList = new List<GameObject>();setFalseGameObject = new List<GameObject>();InitWorld1GameObject();}​void Update(){MaskOn();}​private void OnDrawGizmos(){Gizmos.color = Color.red;Gizmos.DrawWireSphere(transform.position, maskRadius);}/// <summary>/// 初始化获取World1所有子物体/// </summary>void InitWorld1GameObject(){int count = world1.transform.childCount;for (int i = 0; i < count; i++){GetAllChild(world1.transform.GetChild(i).gameObject);}}/// <summary>/// 获取子物体函数/// </summary>/// <param name="father"></param>void GetAllChild(GameObject father){int count = father.transform.childCount;for (int i = 0; i < count; i++){world1GameObjectList.Add(father.transform.GetChild(i).gameObject);}}​/// <summary>/// 遮罩控制/// </summary>void MaskOn(){if (Input.GetKey(KeyCode.Z)){mask.enabled = true; // 开启遮罩maskSP.enabled = true;world2.gameObject.SetActive(true);// 在当前位置画等比例大小的检测圆Collider2D[] collider2Ds = Physics2D.OverlapCircleAll(transform.position, maskRadius, maskLayers);if (collider2Ds.Length > 0){for (int i = 0; i < collider2Ds.Length; i++){GameObject obj = collider2Ds[i].transform.gameObject;if (world1GameObjectList.Contains(obj))// 仅作用与world1{obj.GetComponent<Collider2D>().enabled = false; // 仅取消碰撞体就可以了// 下面根据实际情况修改,此处取消Acid的Damage脚本即可。if (obj.gameObject.GetComponent<Damager>() != null){obj.gameObject.GetComponent<Damager>().enabled = false;}setFalseGameObject.Add(obj);}}}}​if (!Input.GetKey(KeyCode.Z)){mask.enabled = false;maskSP.enabled = false;world2.gameObject.SetActive(false);// 恢复消除的objfor (int i = 0; i < setFalseGameObject.Count; i++){GameObject obj = setFalseGameObject[i].gameObject;obj.GetComponent<Collider2D>().enabled = true; // 恢复碰撞体// 对应上面取消的Acid的Damage脚本if (obj.gameObject.GetComponent<Damager>() != null){obj.gameObject.GetComponent<Damager>().enabled = true;}}setFalseGameObject.Clear();}}}

4.4.2修改Acid

对于World1中Acid,我们是希望Mask能作用于其上的,添加Layer Acid,并修改Acid的Layer。同时,新建父物体命名为Acids,并将Acid设置为Acids的子物体。

4.4.3配置MaskController脚本

4.4.4测试

4.5World2中的拓展

如果说此时我们希望打开魔法门后,能与World2的某个平台交互,而此平台是World1中不存在的,所需修改的地方也很少。

4.5.1整理项目目录

此时项目目录有些许凌乱,我们稍作整理,如下:

即,world1保留所有,不作修改,World2中只保留一些背景以及我们所需的平台PassThroughPlatform。

将平台位置调整为如下:

4.5.2修改平台

如果此时我们运行游戏,会发现平台随着魔法门的开启的出现,但是并没有被遮罩作用,这是因为没有设置平台与遮罩的层级关系。

还是否记得前文提到,MeshRenderer遮罩的实现比较麻烦,而对于Sprite来说,Mask遮罩的实现比较简单,对于这平台来说,就是属于后者情况。

修改平台SpriteRenderer的Mask Interaction属性为visable inside Mask

注意,并非修改平台父物体,而是平台下的子物体。

4.5.3测试

魔术门已对平台产生作用

魔术门关闭时也无法与平台产生交互。

4.6附加

魔术门内外需要提高辨识度。

4.6.1方法:

  1. world2中新建square物体,并命名为Mask

  1. 修改配置Mask

4.6.2测试

五、总结

至此,Unity运用遮罩简单复刻2D平台《Unbound: Worlds Apart》游离于世界之海的双重世界效果已完成。

其他物件的修改基本与上述中毒液以及平台的修改办法一致。

  • 对于world1中需要在魔术门内隐藏的物体,采用配置shader的方式,建议毒液的shader及Material尽量复用;

  • 对于world2中仅在魔术门内可见的物体,采用配置SpriteRenderer层级的方式,需要和平台一样,配置好world2中所有需要被魔术门作用的物体的层级关系。

同时,原作中在开启魔术门后会有额外的能力,如重力反转等,各位只需在MaskOn函数的按键内补充即可,这里不做重点讲解。

六、最后

以上为本人的一些拙见,皆是亲身尝试过且能实现的,可能原作并不是采用我这种方法来实现的,肯定还有其他的更好的方法能实现更完美的效果。针对上述的方法,可能也有处理不当的地方,大家有建议,补充或者更好的方法也欢迎提出!

同时,欢迎转发,创作不易,需标明出处。

工程地址:JN910-14/2D_platform_mask

Unity遮罩简单复刻2D平台《Unbound: Worlds Apart》游离于世界之海的双重世界效果相关推荐

  1. unity简单复刻无敌破坏王

    使用unity简单复刻无敌破坏王总结 国庆突发奇想想简单复刻一下无敌破坏王,这里写一些总结,希望也能对大家有帮助 游戏截图: 基本思路: 时间有限,也只是简单复刻,所以只实现破坏方块的功能,画面算是广 ...

  2. Unity黑魂复刻经典教程心得(三)-CameraController

    CameraController 1.根据角色的位置来计算camera的位置 targetLookAt = new GameObject("targetLookAt").trans ...

  3. Unity黑魂复刻经典教程心得(一)

    b站上傅老师的黑魂复刻教程,是比较好的,傅老师风情幽默,值得种草 https://www.bilibili.com/video/BV1gW411T7yb?p=55

  4. unity生成预制体_【Unity·月之泪复刻】Bloom+摇曳+可交互草地

    最近在做机械纪元的同人游戏,作为名场面-月之泪花田必须有姓名→ω→ 经过两天的缝合(不是),目前效果如下: awsl 对比一下原版-awsl: 机械纪元中的月之泪花田 场景包含: 草地摇曳效果 角色与 ...

  5. 傅老师的unity黑魂复刻学习日记(六)

    内容:角色奔跑,角色转身 在blend tree里面新加一个Motion field 把包里的run动画拖进去 把蓝处消钩取消锁定,设置1为走2为跑. 在palyerinput里面定义一个布尔run, ...

  6. 【Mib自看】黑魂复刻Unity脚本

    [课程地址]B站傅老师Unity课程学习记录,仅代表个人理解. [自看]黑魂复刻Unity脚本 1.移动脚本 设计思路 2.动画 动画脚本:ActorController + 输入脚本:PlayerI ...

  7. 复刻 Unity编辑器 移动的方式

    复刻 Unity编辑器 移动的方式 第一人称移动 自定义键值补充 代码搭载 老规矩,直接上代码: 第一人称移动 using System.Collections; using System.Colle ...

  8. Unity复刻骑砍中的帝国象棋(一)

    Unity复刻骑砍中的帝国象棋(一) 起因和简介 这两天从一款游戏中发现了这么个棋类小游戏,觉得挺有意思,没错,就是下面这个: 作为程序员的我,一下就想到复刻它一下.这个棋类小游戏,我并不知道它确切的 ...

  9. unity中实现经典的2d横版单向跳跃平台

    经常玩2d横版游戏的朋友们相信一定对这种单向跳跃平台很熟悉:我希望我的角色可以通过跳跃跳上平台,然后在平台之上按下键盘的下键后从平台上落下. 那么想要实现这样的效果具体要怎么做呢?我们还是先将想要实现 ...

  10. Unity 创建2D平台游戏开发学习教程

    了解如何使用C#在Unity中创建您的第一款2D平台游戏 你会学到什么 使用Unity创建2D奥运会 使用可脚本化的对象和单一模式 使用良好的编程实践 创造武器和射弹 使用可脚本化的对象和委托模式创建 ...

最新文章

  1. Symfony2 - paginator bundle 复杂查询时候报错解决
  2. JavaScript中的nodeName nodeType nodeValue区别
  3. 按实际价格重估在版本 0, 财政年度 2016 中不可能
  4. 十进制转二进制、二进制转十进制
  5. 【ARM】ARM流水线技术
  6. 小白学python需要多久_小白学Python | 你还在说你入不了门吗
  7. python圈出车牌字符_Python+OpenCV实现车牌字符分割和识别
  8. 解决MAC系统字体发虚,更换默认字体为微软雅黑
  9. 心电监护仪数据图解_心电监护仪数据怎么看
  10. 路由器忘记密码的解决办法
  11. 磁盘阵列服务器上创建虚拟机,UNRAID下虚拟机搭建单机游戏教程
  12. 增量式编码器和绝对式编码器
  13. 青少年计算机等级测试内容,青少年人工智能技术水平测试一级等级考试介绍
  14. 三年白干!程序员因违反《竞业协议》赔偿腾讯 97.6 万元,返还 15.8 万元
  15. windows下批处理文件bat怎么写?
  16. IoT黑板报:美允许4G技术与WiFi共享5G频段
  17. R 语言赋值运算符:`-` , `=`, `-`
  18. http常用请求头与响应头字段详解
  19. mysql使用条件限制乐观锁_mysql乐观锁解决并发问题
  20. 我们学校有计算机房用英语怎么说,根据中英提示,完成下列各句1、他们学校有3个计算机房??......

热门文章

  1. Vue2+Vant2:一个可定制图标的简易扫雷小游戏
  2. 逻辑回归:详细建模流程与例子代码
  3. 【Assertion failed (blockSize % 2 == 1 blockSize > 1) in cv::adaptiveThreshold】
  4. H3CSE园区-LLDP技术
  5. 麦克风阵列技术(转载)
  6. 如何手动备份win10驱动和强制安装驱动
  7. 基于ubuntu20.4安装谷歌拼音中文输入法
  8. SAP WBS预算可通过二种方式配置和使用
  9. 整理12种电脑有趣屏保
  10. 在 mac 系统下播放.csf 视频的方法