【Unity】关于《传送门》复刻的学习,放置传送门

在上一部分,我们已经可以将物品传送至正确的位置。

【Unity】关于《传送门》复刻的学习,物品传送-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_44058587/article/details/138785358?spm=1001.2014.3001.5502在这一部分,我们将实现将传送门发送至墙面。

创建测试地图

首先,向大家推荐ProBuilder插件,该插件可以简单进行建模操作,搭建简易的测试地图。通过该插件创建自己的测试场景。

还没有接触过ProBuilder插件的同学,推荐阅读下面这篇文章后,搭建独属于自己的测试场景。

【游戏开发建模】教你使用Unity ProBuilder制作基础模型,搭建场景原型( 保姆级教程 | Unity 2021最新版)_probuilder教程-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/linxinfa/article/details/124413351我创建的场景如下。

Portal脚本添加功能

为传送门分别添加outline进行区分,这里简单使用RenderQueue完成outline,相关shader如下。

Shader "Portals/Outline"
{
    Properties
    {
		_OutlineColour("Outline Colour", Color) = (1, 1, 1, 1)
		_MaskID("Mask ID", Int) = 1
    }
    SubShader
    {
        Tags 
		{ 
			"RenderType" = "Opaque" 
			"Queue" = "Geometry+2"
		}
        
		Stencil
		{
			Ref 0
			Comp equal
		}

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

			uniform float4 _OutlineColour;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return _OutlineColour;
            }
            ENDCG
        }
    }
}

 为传送门添加Outline效果,如下。

接着为Portal脚本添加关于Outline设置的相关代码,初始化传送门的颜色。

public class Portal : MonoBehaviour
{
    //****省略上文Portal字段

    [SerializeField,Header("Outline设置")]
    private Renderer outlineRenderer;
    [SerializeField]
    private Color portalColor;

    private void Start()
    {
        PlacePortal(wallCollider, transform.position, transform.rotation);
        SetColour(portalColor);
    }
    //****省略上文Portal其他方法
    public void SetColour(Color color)
    {
        material.SetColor("_Colour", color);
        outlineRenderer.material.SetColor("_OutlineColour", color);
    }
}

 在Portal脚本中添加传送门放置方法。

    public void PlacePortal(Collider wallCollider, Vector3 pos, Quaternion rot)
    {
        this.wallCollider = wallCollider;
        transform.position = pos;
        transform.rotation = rot;
        transform.position -= transform.forward * 0.001f;
    }

 创建PortalPlacement脚本

新建PortalPlacement脚本,完成放置传送门位置和角度的计算。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(CameraMove))]
public class PortalPlacement : MonoBehaviour
{
    [SerializeField]
    private PortalPair portals;

    [SerializeField]
    private LayerMask layerMask;

    private CameraMove cameraMove;

    private void Awake()
    {
        cameraMove = GetComponent<CameraMove>();
    }

    private void Update()
    {
        if(Input.GetButtonDown("Fire1"))
        {
            FirePortal(0, transform.position, transform.forward, 250.0f);
        }
        else if (Input.GetButtonDown("Fire2"))
        {
            FirePortal(1, transform.position, transform.forward, 250.0f);
        }
    }

    private void FirePortal(int portalID, Vector3 pos, Vector3 dir, float distance)
    {
        RaycastHit hit;
        Physics.Raycast(pos, dir, out hit, distance, layerMask);

        if(hit.collider != null)
        {
            if (hit.collider.tag == "Portal")
            {
                var inPortal = hit.collider.GetComponent<Portal>();

                if(inPortal == null)
                {
                    return;
                }

                var outPortal = inPortal.GetOtherPortal();

                Vector3 relativePos = inPortal.transform.InverseTransformPoint(hit.point + dir);
                relativePos = Quaternion.Euler(0.0f, 180.0f, 0.0f) * relativePos;
                pos = outPortal.transform.TransformPoint(relativePos);

                Vector3 relativeDir = inPortal.transform.InverseTransformDirection(dir);
                relativeDir = Quaternion.Euler(0.0f, 180.0f, 0.0f) * relativeDir;
                dir = outPortal.transform.TransformDirection(relativeDir);

                distance -= Vector3.Distance(pos, hit.point);

                FirePortal(portalID, pos, dir, distance);

                return;
            }

            var cameraRotation = cameraMove.TargetRotation;

            var portalRight = cameraRotation * Vector3.right;
            
            if(Mathf.Abs(portalRight.x) >= Mathf.Abs(portalRight.z))
            {
                portalRight = (portalRight.x >= 0) ? Vector3.right : -Vector3.right;
            }
            else
            {
                portalRight = (portalRight.z >= 0) ? Vector3.forward : -Vector3.forward;
            }

            var portalForward = -hit.normal;
            var portalUp = -Vector3.Cross(portalRight, portalForward);

            var portalRotation = Quaternion.LookRotation(portalForward, portalUp);

            portals.Portals[portalID].PlacePortal(hit.collider, hit.point, portalRotation);

        }
    }
}

创建PortalPair脚本,便于管理传送门,并确保拥有两个传送门。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PortalPair : MonoBehaviour
{
    public Portal[] Portals { private set; get; }

    private void Awake()
    {
        Portals = GetComponentsInChildren<Portal>();

        if(Portals.Length != 2)
        {
            Debug.LogError("PortalPair children must contain exactly two Portal components in total.");
        }
    }
}

脚本设置

添加Layer用于射线检测。

 PortalPair脚本,应创建空物体,并使两个传送门为其子物体。

最终效果 

目前完成传送门的简单放置,放置传送门穿模问题将在后文解决。

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity 2D中的传送代码需要分为两部分:传送进入和传送出来。 传送进入: 首先,在场景中放置两个传送用的“Collider”(例如Box Collider 2D)。 在传送上,可以通过添加“Tag”(例如“Portal”)和“Layer”来设置传送。 创建一个脚本(例如“PortalEnter.cs”),并将其附加到玩家控制的对象上。 在该脚本中,可以使用以下代码来检测是否进入了传送: ``` void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("Portal")) { //传送的代码 } } ``` 传送出来: 与传送进入不同,传送出来需要在另一个场景中创建另一个传送并在脚本中设置其传送目的地。 在传送的目的地,同样需要创建一个脚本(例如“PortalExit.cs”),并将其附加到玩家控制的对象上。 在该脚本中,可以使用以下代码来检测是否从传送出来: ``` void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("Portal")) { //传送的代码 } } ``` 传送的代码: 首先,需要获取场景中的另一个传送: ``` GameObject exitPortal = GameObject.FindGameObjectWithTag("Portal"); ``` 然后,可以通过以下代码将玩家传送传送的目的地: ``` player.transform.position = exitPortal.transform.position; ``` 完整代码示例: PortalEnter.cs ``` using UnityEngine; public class PortalEnter : MonoBehaviour { GameObject exitPortal; void Start() { exitPortal = GameObject.FindGameObjectWithTag("Portal"); } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("Portal")) { Debug.Log("Enter portal!"); PlayerControl player = other.gameObject.GetComponent<PlayerControl>(); player.isTransporting = true; player.transform.position = exitPortal.transform.position; } } } ``` PortalExit.cs ``` using UnityEngine; public class PortalExit : MonoBehaviour { GameObject enterPortal; void Start() { enterPortal = GameObject.FindGameObjectWithTag("Portal"); } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("Player")) { Debug.Log("Exit portal!"); PlayerControl player = other.gameObject.GetComponent<PlayerControl>(); if (player.isTransporting) { player.isTransporting = false; } } } } ``` 需要注意的是,以上代码只是传送的简单示例,还需要根据具体情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值