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

posted on 2020-05-28 16:48  考拉宝贝  阅读(5490)  评论(0编辑  收藏  举报

导航