ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Unity3D_用鼠标选择游戏物体_在Game中实现Scene中的选中效果

2020-05-28 16:55:10  阅读:246  来源: 互联网

标签:Unity3D outputStream 物体 Scene inputTriangle Game vPositionOs INPUT Append


本示例基于 Unity2018.4.11f1,示例下载在本篇博客结尾处。

一、创建游戏物体(示例中创建了 Unity 中五个基本物体)

二、创建脚本 CreateMouseRay - 将该脚本挂载到摄像机上(挂载到其他游戏物体上也可以,建议挂载到相机上)

三、创建Shader和材质球(如下图)

四、本实例涉及到的其他技术点:

  1. 基于鼠标位置,创建从相机指向鼠标的射线(Scene视图可见)

五、实现思路:

  1. 创建一个物体,该物体带有边缘效果的材质球,取消碰撞器,隐藏该物体
  2. 基于鼠标创建射线,得到射线检测到的物体
  3. 将射线检测到的物体的坐标、旋转、缩放,赋值给第 1 步创建的物体,并取消该物体的隐藏,更换材质球
  4. 检测到的物体不同(可以通过name判断)时,更新边缘效果物体的 Transform,返还刚刚被选中物体的材质球
  5. 射线检测不到物体时隐藏边缘效果物体,返还刚刚被选中物体的材质球

六、脚本 和 Shader

C#脚本 CreateMouseRay

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

public class CreateMouseRay : MonoBehaviour
{
    // 鼠标位置在世界坐标系中的 实例
    private Transform mousePoint;
    // 高亮物体
    private Transform highLightObj;
    // 物体本身的材质球
    private Material oldMaterial;
    // 当前射线检测到的物体
    private GameObject nowObj;
    void Start()
    {
        // 鼠标的屏幕坐标转成世界坐标的点
        mousePoint = GameObject.CreatePrimitive(PrimitiveType.Sphere).GetComponent<Transform>();
        Destroy(mousePoint.GetComponent<Collider>());
        mousePoint.localScale = Vector3.one * 0.1f;

        // 用于边缘效果展示的物体
        highLightObj = GameObject.CreatePrimitive(PrimitiveType.Sphere).GetComponent<Transform>();
        Destroy(highLightObj.GetComponent<Collider>());
        highLightObj.GetComponent<MeshRenderer>().material = Resources.Load<Material>("MT_Silhouette");
        highLightObj.gameObject.SetActive(false);
    }

    void Update()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit raycastHit = new RaycastHit();
        if (Physics.Raycast(ray, out raycastHit))
        {
            if (!nowObj || nowObj.name != raycastHit.transform.name)
            {
                if (nowObj)
                {
                    // 换回原来的材质
                    nowObj.GetComponent<MeshRenderer>().material = oldMaterial;
                }
                // 射线当前检测到的物体
                nowObj = raycastHit.transform.gameObject;
                // 更换材质球
                oldMaterial = raycastHit.transform.GetComponent<MeshRenderer>().material;
                nowObj.GetComponent<MeshRenderer>().material = Resources.Load<Material>("MT_HighLinght");
                // 显示选中效果
                highLightObj.position = raycastHit.transform.position;
                highLightObj.rotation = raycastHit.transform.rotation;
                highLightObj.localScale = raycastHit.transform.localScale;
                highLightObj.GetComponent<MeshFilter>().mesh = raycastHit.collider.GetComponent<MeshFilter>().mesh;
                highLightObj.gameObject.SetActive(true);
            }
        }
        else
        {
            if (highLightObj.gameObject.activeSelf)
            {
                // 隐藏选中效果
                highLightObj.gameObject.SetActive(false);
                // 换回原来的材质
                nowObj.GetComponent<MeshRenderer>().material = oldMaterial;
                // 置空射线检测到的物体
                nowObj = null;
            }
        }
        Debug.DrawRay(Camera.main.transform.position, GetMousePositionOnWorld() - Camera.main.transform.position, Color.red);
        mousePoint.position = GetMousePositionOnWorld();
    }

    private Vector3 GetMousePositionOnWorld()
    {
        Vector3 mousePos = Input.mousePosition;
        // Z 值不能为零,Z 值为零该方法返回值为相机的世界坐标
        // 鼠标坐标值转换为世界坐标时,该方法返回值的 Z  值为:相机的 Z 值加上下面一行代码的赋值
        // 例如:相机 Z 值为-10,经过下面一行代码赋值后,该方法返回值的 Z 值为 0 
        mousePos.z = 10;
        return Camera.main.ScreenToWorldPoint(mousePos);
    }
}

Shader

//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: Used to show the outline of the object
//
//=============================================================================
// UNITY_SHADER_NO_UPGRADE
Shader "Silhouette"
{
    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    Properties
    {
        g_vOutlineColor( "Outline Color", Color ) = ( .5, .5, .5, 1 )
        g_flOutlineWidth( "Outline width", Range ( .001, 0.03 ) ) = .005
        g_flCornerAdjust( "Corner Adjustment", Range( 0, 2 ) ) = .5
    }

    //-------------------------------------------------------------------------------------------------------------------------------------------------------------
    CGINCLUDE

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        #pragma target 5.0

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        #include "UnityCG.cginc"

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        float4 g_vOutlineColor;
        float g_flOutlineWidth;
        float g_flCornerAdjust;

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        struct VS_INPUT
        {
            float4 vPositionOs : POSITION;
            float3 vNormalOs : NORMAL;
        };

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        struct PS_INPUT
        {
            float4 vPositionOs : TEXCOORD0;
            float3 vNormalOs : TEXCOORD1;
            float4 vPositionPs : SV_POSITION;
        };

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        PS_INPUT MainVs( VS_INPUT i )
        {
            PS_INPUT o;
            o.vPositionOs.xyzw = i.vPositionOs.xyzw;
            o.vNormalOs.xyz = i.vNormalOs.xyz;
#if UNITY_VERSION >= 540
            o.vPositionPs = UnityObjectToClipPos( i.vPositionOs.xyzw );
#else
            o.vPositionPs = mul( UNITY_MATRIX_MVP, i.vPositionOs.xyzw );
#endif
            return o;
        }

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        PS_INPUT Extrude( PS_INPUT vertex )
        {
            PS_INPUT extruded = vertex;

            // Offset along normal in projection space
            float3 vNormalVs = mul( ( float3x3 )UNITY_MATRIX_IT_MV, vertex.vNormalOs.xyz );
            float2 vOffsetPs = TransformViewToProjection( vNormalVs.xy );
            vOffsetPs.xy = normalize( vOffsetPs.xy );

            // Calculate position
#if UNITY_VERSION >= 540
            extruded.vPositionPs = UnityObjectToClipPos( vertex.vPositionOs.xyzw );
#else
            extruded.vPositionPs = mul( UNITY_MATRIX_MVP, vertex.vPositionOs.xyzw );
#endif
            extruded.vPositionPs.xy += vOffsetPs.xy * extruded.vPositionPs.w * g_flOutlineWidth;

            return extruded;
        }

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        [maxvertexcount(18)]
        void ExtrudeGs( triangle PS_INPUT inputTriangle[3], inout TriangleStream<PS_INPUT> outputStream )
        {
            float3 a = normalize(inputTriangle[0].vPositionOs.xyz - inputTriangle[1].vPositionOs.xyz);
            float3 b = normalize(inputTriangle[1].vPositionOs.xyz - inputTriangle[2].vPositionOs.xyz);
            float3 c = normalize(inputTriangle[2].vPositionOs.xyz - inputTriangle[0].vPositionOs.xyz);

            inputTriangle[0].vNormalOs = inputTriangle[0].vNormalOs + normalize( a - c) * g_flCornerAdjust;
            inputTriangle[1].vNormalOs = inputTriangle[1].vNormalOs + normalize(-a + b) * g_flCornerAdjust;
            inputTriangle[2].vNormalOs = inputTriangle[2].vNormalOs + normalize(-b + c) * g_flCornerAdjust;

            PS_INPUT extrudedTriangle0 = Extrude( inputTriangle[0] );
            PS_INPUT extrudedTriangle1 = Extrude( inputTriangle[1] );
            PS_INPUT extrudedTriangle2 = Extrude( inputTriangle[2] );

            outputStream.Append( inputTriangle[0] );
            outputStream.Append( extrudedTriangle0 );
            outputStream.Append( inputTriangle[1] );
            outputStream.Append( extrudedTriangle0 );
            outputStream.Append( extrudedTriangle1 );
            outputStream.Append( inputTriangle[1] );

            outputStream.Append( inputTriangle[1] );
            outputStream.Append( extrudedTriangle1 );
            outputStream.Append( extrudedTriangle2 );
            outputStream.Append( inputTriangle[1] );
            outputStream.Append( extrudedTriangle2 );
            outputStream.Append( inputTriangle[2] );

            outputStream.Append( inputTriangle[2] );
            outputStream.Append( extrudedTriangle2 );
            outputStream.Append(inputTriangle[0]);
            outputStream.Append( extrudedTriangle2 );
            outputStream.Append( extrudedTriangle0 );
            outputStream.Append( inputTriangle[0] );
        }

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        fixed4 MainPs( PS_INPUT i ) : SV_Target
        {
            return g_vOutlineColor;
        }

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        fixed4 NullPs( PS_INPUT i ) : SV_Target
        {
            return float4( 1.0, 0.0, 1.0, 1.0 );
        }

    ENDCG

    SubShader
    {
        Tags { "RenderType"="Outline" "Queue" = "Geometry-1"  }

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        // Render the object with stencil=1 to mask out the part that isn't the silhouette
        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        Pass
        {
            Tags { "LightMode" = "Always" }
            ColorMask 0
            Cull Off
            ZWrite Off
            Stencil
            {
                Ref 1
                Comp always
                Pass replace
            }

            CGPROGRAM
                #pragma vertex MainVs
                #pragma fragment NullPs
            ENDCG
        }

        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        // Render the outline by extruding along vertex normals and using the stencil mask previously rendered. Only render depth, so that the final pass executes
        // once per fragment (otherwise alpha blending will look bad).
        //-------------------------------------------------------------------------------------------------------------------------------------------------------------
        Pass
        {
            Tags { "LightMode" = "Always" }
            Cull Off
            ZWrite On
            Stencil
            {
                Ref 1
                Comp notequal
                Pass keep
                Fail keep
            }

            CGPROGRAM
                #pragma vertex MainVs
                #pragma geometry ExtrudeGs
                #pragma fragment MainPs
            ENDCG
        }
    }
}

点击下载示例文件,解压后为 unitypackage 文件  https://files.cnblogs.com/files/kao-la-bao-bei/Select.rar

标签:Unity3D,outputStream,物体,Scene,inputTriangle,Game,vPositionOs,INPUT,Append
来源: https://www.cnblogs.com/kao-la-bao-bei/p/12979264.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有