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

零、前言

最近无意中发现了《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; // 遮罩sprite
     public 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);
             // 恢复消除的obj
             for (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

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值