【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
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值