Unity3D_用鼠標選擇游戲物體_在Game中實現Scene中的選中效果


本示例基於 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM