블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2838)
Unity3D (886)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (188)
협업 (64)
3DS Max (3)
Game (12)
Utility (140)
Etc (99)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (52)
Android (16)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (19)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

[링크] https://docs.unity3d.com/6000.2/Documentation/Manual/scripting-symbol-reference.html

 

----

Platform symbols

Unity automatically defines certain symbols based on the authoring and build target platform. These are as follows:

DefineFunction

UNITY_EDITOR Scripting symbol to call Unity Editor scripts
 from your game code.
UNITY_EDITOR_WIN Scripting symbol for Editor code on Windows.
UNITY_EDITOR_OSX Scripting symbol for Editor code in macOS.
UNITY_EDITOR_LINUX Scripting symbol for Editor code on Linux.
UNITY_EMBEDDED_LINUX Scripting symbol for embedded Linux.
UNITY_QNX Scripting symbol for QNX.
UNITY_STANDALONE_OSX Scripting symbol to compile or execute code specifically for macOS (including Universal, PPC and Intel architectures).
UNITY_STANDALONE_WIN Scripting symbol for compiling/executing code specifically for Windows standalone applications.
UNITY_STANDALONE_LINUX Scripting symbol for compiling/executing code specifically for Linux standalone applications.
UNITY_STANDALONE Scripting symbol for compiling/executing code for any standalone platform (Mac OS X, Windows or Linux).
UNITY_SERVER Scripting symbol for compiling/executing code for a dedicated server (macOS, Windows or Linux).
UNITY_IOS Scripting symbol for compiling/executing code for the iOS platform.
UNITY_ANDROID Scripting symbol for the Android platform.
UNITY_TVOS Scripting symbol for the Apple TV platform.
UNITY_VISIONOS Scripting symbol for the VisionOS platform.
UNITY_WSA Scripting symbol for Universal Windows Platform.
UNITY_WSA_10_0 Scripting symbol for Universal Windows Platform.
UNITY_WEBGL Scripting symbol for Web.
UNITY_FACEBOOK_INSTANT_GAMES Scripting symbol for the Facebook Instant Games platform.
UNITY_ANALYTICS Scripting symbol for calling Unity Analytics
 methods from your game code.
UNITY_ASSERTIONS Scripting symbol for assertions control process.
UNITY_64 Scripting symbol for 64-bit platforms. In practice this should not be used because it does not work on all 64-bit architectures and different CPU architectures on a given platform can share the same compiled assemblies. To execute code conditionally based on architecture, use a standard if statement that checks IntPtr.Size, which is 4 in a 32-bit process and 8 in a 64-bit process. For an example, refer to Alternatives to directives.

 

Unity Editor version symbols

Unity automatically defines certain scripting symbols based on the version of the Unity Editor that you’re currently using.

Given a version number X.Y.Z (for example, 6000.0.33), Unity exposes three global scripting symbols in the following formats: UNITY_X, UNITY_X_Y and UNITY_X_Y_Z.

Here is an example of scripting symbols exposed in Unity 6000.0.33:

DefineFunction

UNITY_6000 Scripting symbol for the release version of Unity 6, exposed in every 6000.Y.Z release.
UNITY_6000_0 Scripting symbol for the major version of Unity 6.0, exposed in every 6000.0.Z release.
UNITY_6000_0_33 Scripting symbol for the minor version of Unity 6000.0.33.

You can also compile code selectively based on the earliest version of Unity required to compile or execute a section of code snippet. Following the same version format describd previously (X.Y), Unity exposes one global #define in the format UNITY_X_Y_OR_NEWER (for example, UNITY_6000_0_OR_NEWER), that you can use for this purpose.

 

Other symbols

The other symbols Unity defines are:

DefineFunction

CSHARP_7_3_OR_NEWER Defined when building scripts with support for C# 7.3 or newer.
ENABLE_MONO Scripting back end #define for Mono.
ENABLE_IL2CPP Scripting back end #define for IL2CPP
.
ENABLE_VR Defined when the target build platform supports VR
. Doesn’t imply that VR is currently enabled or that the necessary plug-ins
 and packages needed to support VR are installed.
NET_2_0 Defined when building scripts against .NET 2.0 API compatibility level on Mono and IL2CPP.
NET_2_0_SUBSET Defined when building scripts against .NET 2.0 Subset API compatibility level on Mono and IL2CPP.
NET_LEGACY Defined when building scripts against .NET 2.0 or .NET 2.0 Subset API compatibility level on Mono and IL2CPP.
NET_4_6 Defined when building scripts against .NET 4.x API compatibility level on Mono and IL2CPP.
NET_STANDARD_2_0 Defined when building scripts against .NET Standard 2.0 API compatibility level on Mono and IL2CPP.
NET_STANDARD_2_1 Defined when building scripts against .NET Standard 2.1 API compatibility level on Mono and IL2CPP.
NET_STANDARD Defined when building scripts against .NET Standard 2.1 API compatibility level on Mono and IL2CPP.
NETSTANDARD2_1 Defined when building scripts against .NET Standard 2.1 API compatibility level on Mono and IL2CPP.
NETSTANDARD Defined when building scripts against .NET Standard 2.1 API compatibility level on Mono and IL2CPP.
ENABLE_WINMD_SUPPORT Defined when Windows Runtime support is enabled on IL2CPP. For more information, refer to WinRT API in C# scripts for UWP.
ENABLE_INPUT_SYSTEM Defined when the Input System package is enabled in Player Settings
.
ENABLE_LEGACY_INPUT_MANAGER Defined when the legacy Input Manager
 is enabled in Player Settings.
DEVELOPMENT_BUILD Defined when your script is running in a Player which was built with the Development Build option enabled.

This define only reflects whether the development build option was enabled at the time of the build. To know whether your script is running in the development build mode, use Debug.isDebugBuild. DEVELOPMENT_BUILD isn’t sufficient to determine whether you’re currently running in a development build because most platforms allow changing between development and non-development build without rebuilding the project. However, on some platforms, Unity doesn’t support switching between development and non-development builds in the Editor and requires you to switch after the build is complete. For example, on Windows, you can choose the Create Visual Studio solution option to choose whether you want a development or non-development build in Visual Studio. Switching in Visual Studio doesn’t recompile your scripts and therefore, it will not reevaluate scripting defines. You can also switch from the final game build to a development build by swapping UnityPlayer.dll in the game build with the one from a development build for debugging live game builds.
UNITY_CLOUD_BUILD Defined when the project is built with Unity Build Automation
.

Note: The DEBUG symbol is predefined in C# and in Unity using the directive #if DEBUG is equivalent to #if UNITY_EDITOR || DEVELOPMENT_BUILD

Additional resources

반응형
Posted by blueasa
, |

Define Invoker

Unity3D/Script / 2025. 8. 21. 17:14

Unity 6000.1.15f1

----

 

유니티 Platform Define Symbols를 직접 사용하지 않고 Invoke Class로 묶어서

내가 작성한 코드가 Define 때문에 비활성화 되어서 빌드 할 때 에러를 보게 되는 경우를 방지하기 위해 제작(Cursor AI 시킴)

 

[파일]

DefineInvoker.cs
0.02MB
DefineInvokerExample.cs
0.00MB

 

 

[DefineInvoker.cs]

using System;

/// <summary>
/// Unity platform define을 사용하여 플랫폼별 조건부 실행을 지원하는 클래스
/// </summary>
public static class DefineInvoker
{
    /// <summary>
    /// 메서드 체이닝을 지원하는 InvokerChain 클래스
    /// </summary>
    public class InvokerChain
    {
        /// <summary>
        /// Android 플랫폼에서만 실행되는 코드 (에디터 제외)
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain Android(Action action)
        {
#if UNITY_ANDROID && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// Android 플랫폼에서만 실행되는 코드 (에디터 제외, 반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T Android<T>(Func<T> func)
        {
#if UNITY_ANDROID && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// iOS 플랫폼에서만 실행되는 코드 (에디터 제외)
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain iOS(Action action)
        {
#if UNITY_IOS && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// iOS 플랫폼에서만 실행되는 코드 (에디터 제외, 반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T iOS<T>(Func<T> func)
        {
#if UNITY_IOS && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// Windows 플랫폼에서만 실행되는 코드
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain Windows(Action action)
        {
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// Windows 플랫폼에서만 실행되는 코드 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T Windows<T>(Func<T> func)
        {
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// macOS 플랫폼에서만 실행되는 코드
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain macOS(Action action)
        {
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// macOS 플랫폼에서만 실행되는 코드 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T macOS<T>(Func<T> func)
        {
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// Linux 플랫폼에서만 실행되는 코드
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain Linux(Action action)
        {
#if UNITY_STANDALONE_LINUX && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// Linux 플랫폼에서만 실행되는 코드 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T Linux<T>(Func<T> func)
        {
#if UNITY_STANDALONE_LINUX && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// WebGL 플랫폼에서만 실행되는 코드
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain WebGL(Action action)
        {
#if UNITY_WEBGL && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// WebGL 플랫폼에서만 실행되는 코드 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T WebGL<T>(Func<T> func)
        {
#if UNITY_WEBGL && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// Unity 에디터에서만 실행되는 코드
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain Editor(Action action)
        {
#if UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// Unity 에디터에서만 실행되는 코드 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T Editor<T>(Func<T> func)
        {
#if UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// Standalone 플랫폼(Windows, macOS, Linux)에서만 실행되는 코드
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain Standalone(Action action)
        {
#if UNITY_STANDALONE && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// Standalone 플랫폼(Windows, macOS, Linux)에서만 실행되는 코드 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T Standalone<T>(Func<T> func)
        {
#if UNITY_STANDALONE && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// 모바일 플랫폼(Android, iOS)에서만 실행되는 코드 (에디터 제외)
        /// </summary>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain Mobile(Action action)
        {
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
            action?.Invoke();
#endif
            return this;
        }

        /// <summary>
        /// 모바일 플랫폼(Android, iOS)에서만 실행되는 코드 (에디터 제외, 반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T Mobile<T>(Func<T> func)
        {
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
            return func != null ? func.Invoke() : default(T);
#else
            return default(T);
#endif
        }

        /// <summary>
        /// 커스텀 조건문을 사용하여 실행
        /// </summary>
        /// <param name="condition">실행 조건</param>
        /// <param name="action">실행할 액션</param>
        /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
        public InvokerChain When(bool condition, Action action)
        {
            if (condition)
            {
                action?.Invoke();
            }
            return this;
        }

        /// <summary>
        /// 커스텀 조건문을 사용하여 실행 (반환값 있음)
        /// </summary>
        /// <typeparam name="T">반환 타입</typeparam>
        /// <param name="condition">실행 조건</param>
        /// <param name="func">실행할 함수</param>
        /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
        public T When<T>(bool condition, Func<T> func)
        {
            if (condition)
            {
                return func != null ? func.Invoke() : default(T);
            }
            return default(T);
        }
    }

    // 정적 메서드들 - 단일 플랫폼 실행용
    
    /// <summary>
    /// Android 플랫폼에서만 실행되는 코드 (에디터 제외)
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Android(Action action)
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// Android 플랫폼에서만 실행되는 코드 (에디터 제외, 반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T Android<T>(Func<T> func)
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// iOS 플랫폼에서만 실행되는 코드 (에디터 제외)
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain iOS(Action action)
    {
#if UNITY_IOS && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// iOS 플랫폼에서만 실행되는 코드 (에디터 제외, 반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T iOS<T>(Func<T> func)
    {
#if UNITY_IOS && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// Windows 플랫폼에서만 실행되는 코드
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Windows(Action action)
    {
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// Windows 플랫폼에서만 실행되는 코드 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T Windows<T>(Func<T> func)
    {
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// macOS 플랫폼에서만 실행되는 코드
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain macOS(Action action)
    {
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// macOS 플랫폼에서만 실행되는 코드 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T macOS<T>(Func<T> func)
    {
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// Linux 플랫폼에서만 실행되는 코드
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Linux(Action action)
    {
#if UNITY_STANDALONE_LINUX && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// Linux 플랫폼에서만 실행되는 코드 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T Linux<T>(Func<T> func)
    {
#if UNITY_STANDALONE_LINUX && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// WebGL 플랫폼에서만 실행되는 코드
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain WebGL(Action action)
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// WebGL 플랫폼에서만 실행되는 코드 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T WebGL<T>(Func<T> func)
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// Unity 에디터에서만 실행되는 코드
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Editor(Action action)
    {
#if UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// Unity 에디터에서만 실행되는 코드 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T Editor<T>(Func<T> func)
    {
#if UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// Standalone 플랫폼(Windows, macOS, Linux)에서만 실행되는 코드
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Standalone(Action action)
    {
#if UNITY_STANDALONE && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// Standalone 플랫폼(Windows, macOS, Linux)에서만 실행되는 코드 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T Standalone<T>(Func<T> func)
    {
#if UNITY_STANDALONE && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// 모바일 플랫폼(Android, iOS)에서만 실행되는 코드 (에디터 제외)
    /// </summary>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Mobile(Action action)
    {
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
        action?.Invoke();
#endif
        return new InvokerChain();
    }

    /// <summary>
    /// 모바일 플랫폼(Android, iOS)에서만 실행되는 코드 (에디터 제외, 반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T Mobile<T>(Func<T> func)
    {
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
        return func != null ? func.Invoke() : default(T);
#else
        return default(T);
#endif
    }

    /// <summary>
    /// 커스텀 조건문을 사용하여 실행
    /// </summary>
    /// <param name="condition">실행 조건</param>
    /// <param name="action">실행할 액션</param>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain When(bool condition, Action action)
    {
        if (condition)
        {
            action?.Invoke();
        }
        return new InvokerChain();
    }

    /// <summary>
    /// 커스텀 조건문을 사용하여 실행 (반환값 있음)
    /// </summary>
    /// <typeparam name="T">반환 타입</typeparam>
    /// <param name="condition">실행 조건</param>
    /// <param name="func">실행할 함수</param>
    /// <returns>함수의 실행 결과 (조건이 맞지 않으면 default(T))</returns>
    public static T When<T>(bool condition, Func<T> func)
    {
        if (condition)
        {
            return func != null ? func.Invoke() : default(T);
        }
        return default(T);
    }

    /// <summary>
    /// 체이닝을 시작하기 위한 메서드
    /// </summary>
    /// <returns>체이닝을 위한 InvokerChain 인스턴스</returns>
    public static InvokerChain Chain()
    {
        return new InvokerChain();
    }
}

 

[DefineInvokerExample.cs]

using UnityEngine;

/// <summary>
/// DefineInvoker 사용 예시 클래스
/// </summary>
public class DefineInvokerExample : MonoBehaviour
{
    void Start()
    {
        // 예시 1: 단일 플랫폼 실행
        DefineInvoker.Android(() => 
        {
            Debug.Log("Android 플랫폼에서 실행됨");
            // Android 전용 코드
        });

        DefineInvoker.iOS(() => 
        {
            Debug.Log("iOS 플랫폼에서 실행됨");
            // iOS 전용 코드
        });

        DefineInvoker.Editor(() => 
        {
            Debug.Log("Unity 에디터에서 실행됨");
            // 에디터 전용 코드
        });

        // 예시 2: 메서드 체이닝 사용
        DefineInvoker.Android(() => 
            {
                Debug.Log("Android 초기화");
                InitializeAndroid();
            })
            .iOS(() => 
            {
                Debug.Log("iOS 초기화");
                InitializeiOS();
            })
            .Windows(() => 
            {
                Debug.Log("Windows 초기화");
                InitializeWindows();
            })
            .Editor(() => 
            {
                Debug.Log("에디터 초기화");
                InitializeEditor();
            });

        // 예시 3: Chain() 메서드로 시작하는 체이닝
        DefineInvoker.Chain()
            .Mobile(() => 
            {
                Debug.Log("모바일 플랫폼 공통 설정");
                SetupMobileSettings();
            })
            .Standalone(() => 
            {
                Debug.Log("PC 플랫폼 공통 설정");
                SetupDesktopSettings();
            })
            .WebGL(() => 
            {
                Debug.Log("WebGL 플랫폼 설정");
                SetupWebGLSettings();
            });

        // 예시 4: 커스텀 조건과 함께 사용
        bool isDebugBuild = Debug.isDebugBuild;
        DefineInvoker.When(isDebugBuild, () => 
        {
            Debug.Log("디버그 빌드에서만 실행되는 코드");
            EnableDebugFeatures();
        })
        .Android(() => 
        {
            Debug.Log("Android + 추가 설정");
        });

        // 예시 5: 복잡한 조건부 실행
        DefineInvoker.Chain()
            .When(Application.platform == RuntimePlatform.Android, () => 
            {
                Debug.Log("런타임에서 Android 플랫폼 확인");
            })
            .When(SystemInfo.deviceType == DeviceType.Handheld, () => 
            {
                Debug.Log("핸드헬드 기기에서 실행");
            })
            .Editor(() => 
            {
                Debug.Log("에디터에서 테스트 실행");
            });

        // 예시 6: Func 사용 (반환값 있는 함수)
        string platformName = DefineInvoker.Android(() => 
        {
            return "Android 플랫폼";
        });
        Debug.Log($"플랫폼 이름: {platformName}");

        int playerLevel = DefineInvoker.Mobile(() => 
        {
            return GetMobilePlayerLevel();
        });
        Debug.Log($"모바일 플레이어 레벨: {playerLevel}");

        // 예시 7: 여러 플랫폼에서 다른 값 반환
        string apiUrl = DefineInvoker.Android(() => "https://android-api.game.com")
                     ?? DefineInvoker.iOS(() => "https://ios-api.game.com")
                     ?? DefineInvoker.Editor(() => "https://dev-api.game.com")
                     ?? "https://default-api.game.com";
        Debug.Log($"API URL: {apiUrl}");

        // 예시 8: 조건부 값 반환
        bool isDevelopment = DefineInvoker.Editor(() => true);
        Debug.Log($"개발 모드: {isDevelopment}");
    }

    private void InitializeAndroid()
    {
        // Android 초기화 로직
        Debug.Log("Android 특화 초기화 완료");
    }

    private void InitializeiOS()
    {
        // iOS 초기화 로직
        Debug.Log("iOS 특화 초기화 완료");
    }

    private void InitializeWindows()
    {
        // Windows 초기화 로직
        Debug.Log("Windows 특화 초기화 완료");
    }

    private void InitializeEditor()
    {
        // 에디터 초기화 로직
        Debug.Log("에디터 특화 초기화 완료");
    }

    private void SetupMobileSettings()
    {
        // 모바일 공통 설정
        Debug.Log("모바일 공통 설정 적용");
        Screen.sleepTimeout = SleepTimeout.NeverSleep;
    }

    private void SetupDesktopSettings()
    {
        // 데스크탑 공통 설정
        Debug.Log("데스크탑 공통 설정 적용");
        Screen.fullScreen = true;
    }

    private void SetupWebGLSettings()
    {
        // WebGL 설정
        Debug.Log("WebGL 특화 설정 적용");
    }

    private void EnableDebugFeatures()
    {
        // 디버그 기능 활성화
        Debug.Log("디버그 기능 활성화됨");
    }

    private int GetMobilePlayerLevel()
    {
        // 모바일 플레이어 레벨 반환 예시
        return 42;
    }
}

 

 

[참조] https://docs.unity3d.com/6000.2/Documentation/Manual/scripting-symbol-reference.html

 

반응형
Posted by blueasa
, |

[링크] https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample

 

GitHub - ColinLeung-NiloCat/UnityURPToonLitShaderExample: A very simple toon lit shader example, for you to learn writing custom

A very simple toon lit shader example, for you to learn writing custom lit shader in Unity URP - ColinLeung-NiloCat/UnityURPToonLitShaderExample

github.com

 

반응형
Posted by blueasa
, |

Unity 6000.1.11f1

URP

InputSystem 1.14.1

NGUI 2025.07.01

----

 

Legacy Input System을 new Input System으로 변환하면서 기존 Third Party 에셋들이 기존 Legacy Input을 계속 사용하고 있어서(특히 NGUI)

에러나는 부분을 가능하면 손을 덜 대고 해결하기 위해서,

Legacy Input System을 New Input System으로 Wrapping하는 클래스를 만들었다.

(왠만한 건 다 Wrapping 한 것 같은데 혹시나 빠진게 있을수도 있다.)

 

Activate Input Handling을 New Input System으로 변경하고 아래 스크립트를 Plugins 폴더에 넣어주면 된다.

 

[파일] 

Input.cs
0.03MB

 

[코드]

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using Debug = UnityEngine.Debug;

// 별칭 지정
using InputSystemTouchPhase = UnityEngine.InputSystem.TouchPhase;
using LegacyTouchPhase = UnityEngine.TouchPhase;
using InputSystemGyroscope = UnityEngine.InputSystem.Gyroscope;



/// <summary>
/// Legacy Input System을 New Input System으로 Wrapping하는 클래스.
/// </summary>
public static class Input
{
    #region 필드

    /// <summary>
    /// 가속도계 이벤트를 나타냅니다.
    /// </summary>
    public struct AccelerationEvent
    {
        public Vector3 acceleration;
        public float deltaTime;
    }

    /// <summary>
    /// Legacy Gyroscope API를 모방하는 래퍼 클래스입니다.
    /// </summary>
    public class Gyroscope
    {
        private readonly InputSystemGyroscope device;
        private readonly GravitySensor gravityDevice;
        private readonly AttitudeSensor attitudeDevice;

        internal Gyroscope(InputSystemGyroscope device)
        {
            this.device = device;
            this.gravityDevice = GravitySensor.current;
            this.attitudeDevice = AttitudeSensor.current;
        }

        /// <summary>
        /// 자이로스코프 활성화 여부입니다.
        /// </summary>
        public bool enabled
        {
            get => device != null && device.enabled;
            set
            {
                if (device != null && device.enabled != value)
                {
                    if (value)
                    {
                        InputSystem.EnableDevice(device);
                        if (attitudeDevice != null) InputSystem.EnableDevice(attitudeDevice);
                    }
                    else
                    {
                        InputSystem.DisableDevice(device);
                        if (attitudeDevice != null) InputSystem.DisableDevice(attitudeDevice);
                    }
                }
            }
        }

        /// <summary>
        /// 장치의 자세(Attitude)를 반환합니다.
        /// </summary>
        public Quaternion attitude => attitudeDevice?.attitude.ReadValue() ?? Quaternion.identity;

        /// <summary>
        /// 장치의 중력 가속도 벡터를 반환합니다.
        /// </summary>
        public Vector3 gravity => gravityDevice?.gravity.ReadValue() ?? Vector3.zero;

        /// <summary>
        /// 장치의 회전 속도를 반환합니다. (deg/s)
        /// </summary>
        public Vector3 rotationRate => (device?.angularVelocity.ReadValue() ?? Vector3.zero) * Mathf.Rad2Deg;

        /// <summary>
        /// 바이어스가 보정된 장치의 회전 속도를 반환합니다. (New Input System에서는 rotationRate와 동일)
        /// </summary>
        public Vector3 rotationRateUnbiased => rotationRate;

        /// <summary>
        /// 자이로스코프의 업데이트 간격(초)입니다.
        /// </summary>
        public float updateInterval
        {
            get => (device != null && device.samplingFrequency > 0) ? 1.0f / device.samplingFrequency : 0;
            set
            {
                // New Input System에서는 프로그래밍 방식으로 업데이트 간격을 설정할 수 없습니다.
                LogInfo("Gyroscope.updateInterval은 New Input System에서 설정할 수 없습니다.");
            }
        }
    }


    private static readonly Dictionary<KeyCode, Key> keyMapping = new Dictionary<KeyCode, Key>();
    private static readonly Dictionary<string, InputAction> axisActions = new Dictionary<string, InputAction>();
    private static readonly List<Touch> cachedTouches = new List<Touch>();
    private static readonly List<AccelerationEvent> accelerationEventBuffer = new List<AccelerationEvent>();

    private static InputAction anyKeyAction;
    private static float lastTouchUpdateTime;
    private static bool isInitialized;
    private static bool enableDebugLogs = true;
    private static Gyroscope _gyroscope;


    // UIInput 호환성을 위한 필드
    private static readonly StringBuilder inputBuffer = new StringBuilder();
    public static string compositionString { get; private set; } = "";
    public static Vector2 compositionCursorPos { get; set; }

    // 현재 입력 장치들
    private static Mouse currentMouse => Mouse.current;
    private static Keyboard currentKeyboard => Keyboard.current;
    private static Gamepad currentGamepad => Gamepad.current;
    private static Touchscreen currentTouchscreen => Touchscreen.current;
    private static Accelerometer currentAccelerometer => Accelerometer.current;
    private static InputSystemGyroscope currentGyro => InputSystemGyroscope.current;


    private enum KeyState { Down, Up, Held }

    #endregion

    #region 초기화

    static Input()
    {
        Initialize();
    }

    /// <summary>
    /// NewInput 시스템을 명시적으로 초기화합니다.
    /// </summary>
    public static void Initialize()
    {
        if (isInitialized) return;

        try
        {
            LogInfo("NewInput 시스템 초기화 시작...");

            InitializeKeyMapping();
            InitializeDefaultActions();
            SetupAnyKeyAction();
            SetupTextHandling();

#if UNITY_ANDROID || UNITY_IOS
            if (!UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport.enabled)
            {
                UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport.Enable();
                LogInfo("EnhancedTouchSupport 활성화됨.");
            }
            if (currentAccelerometer != null && !currentAccelerometer.enabled)
            {
                InputSystem.EnableDevice(currentAccelerometer);
                LogInfo("가속도계 활성화됨.");
            }
            if (currentGyro != null && !currentGyro.enabled)
            {
                InputSystem.EnableDevice(currentGyro);
                LogInfo("자이로스코프 활성화됨.");
            }
            if (GravitySensor.current != null && !GravitySensor.current.enabled)
            {
                InputSystem.EnableDevice(GravitySensor.current);
                LogInfo("중력 센서 활성화됨.");
            }
            if (AttitudeSensor.current != null && !AttitudeSensor.current.enabled)
            {
                InputSystem.EnableDevice(AttitudeSensor.current);
                LogInfo("자세 센서 활성화됨.");
            }
#endif

            isInitialized = true;
            LogInfo("NewInput 시스템 초기화 완료.");
        }
        catch (Exception ex)
        {
            LogError($"NewInput 초기화 중 오류 발생: {ex.Message}");
        }
    }

    private static void InitializeKeyMapping()
    {
        keyMapping.Clear();
        // 알파벳, 숫자, 특수 키 매핑 (기존과 동일)
        for (int i = 0; i < 26; i++) keyMapping[(KeyCode)((int)KeyCode.A + i)] = Key.A + i;
        for (int i = 0; i < 10; i++) keyMapping[(KeyCode)((int)KeyCode.Alpha0 + i)] = Key.Digit0 + i;
        for (int i = 0; i < 10; i++) keyMapping[(KeyCode)((int)KeyCode.Keypad0 + i)] = Key.Numpad0 + i;
        for (int i = 0; i < 15; i++) keyMapping[(KeyCode)((int)KeyCode.F1 + i)] = Key.F1 + i;
        keyMapping[KeyCode.Backspace] = Key.Backspace;
        keyMapping[KeyCode.Tab] = Key.Tab;
        keyMapping[KeyCode.Return] = Key.Enter;
        keyMapping[KeyCode.Pause] = Key.Pause;
        keyMapping[KeyCode.Escape] = Key.Escape;
        keyMapping[KeyCode.Space] = Key.Space;
        keyMapping[KeyCode.Delete] = Key.Delete;
        keyMapping[KeyCode.LeftShift] = Key.LeftShift;
        keyMapping[KeyCode.RightShift] = Key.RightShift;
        keyMapping[KeyCode.LeftControl] = Key.LeftCtrl;
        keyMapping[KeyCode.RightControl] = Key.RightCtrl;
        keyMapping[KeyCode.LeftAlt] = Key.LeftAlt;
        keyMapping[KeyCode.RightAlt] = Key.RightAlt;
        keyMapping[KeyCode.LeftArrow] = Key.LeftArrow;
        keyMapping[KeyCode.RightArrow] = Key.RightArrow;
        keyMapping[KeyCode.UpArrow] = Key.UpArrow;
        keyMapping[KeyCode.DownArrow] = Key.DownArrow;
        keyMapping[KeyCode.KeypadEnter] = Key.NumpadEnter;
        keyMapping[KeyCode.Insert] = Key.Insert;
        keyMapping[KeyCode.Home] = Key.Home;
        keyMapping[KeyCode.End] = Key.End;
        keyMapping[KeyCode.PageUp] = Key.PageUp;
        keyMapping[KeyCode.PageDown] = Key.PageDown;
    }

    private static void InitializeDefaultActions()
    {
        // 기존 축 액션 정리
        if (axisActions.TryGetValue("Horizontal", out var horizontalAction)) horizontalAction.Disable();
        if (axisActions.TryGetValue("Vertical", out var verticalAction)) verticalAction.Disable();

        // Horizontal 축 설정 (키보드 좌/우 화살표, A/D 키)
        var horizontal = new InputAction("Horizontal", type: InputActionType.Value);
        horizontal.AddCompositeBinding("1DAxis")
            .With("negative", "<Keyboard>/leftArrow")
            .With("positive", "<Keyboard>/rightArrow");
        horizontal.AddCompositeBinding("1DAxis")
            .With("negative", "<Keyboard>/a")
            .With("positive", "<Keyboard>/d");
        horizontal.AddBinding("<Gamepad>/leftStick/x");
        horizontal.Enable();
        axisActions["Horizontal"] = horizontal;

        // Vertical 축 설정 (키보드 상/하 화살표, W/S 키)
        var vertical = new InputAction("Vertical", type: InputActionType.Value);
        vertical.AddCompositeBinding("1DAxis")
            .With("negative", "<Keyboard>/downArrow")
            .With("positive", "<Keyboard>/upArrow");
        vertical.AddCompositeBinding("1DAxis")
            .With("negative", "<Keyboard>/s")
            .With("positive", "<Keyboard>/w");
        vertical.AddBinding("<Gamepad>/leftStick/y");
        vertical.Enable();
        axisActions["Vertical"] = vertical;

        // 마우스 축 설정
        CreateAxisAction("Mouse X", "<Mouse>/delta/x");
        CreateAxisAction("Mouse Y", "<Mouse>/delta/y");
        CreateAxisAction("Mouse ScrollWheel", "<Mouse>/scroll/y");
    }

    private static void SetupAnyKeyAction()
    {
        anyKeyAction?.Disable();
        anyKeyAction = new InputAction("AnyKey", type: InputActionType.PassThrough);
        anyKeyAction.AddBinding("<Keyboard>/anyKey");
        anyKeyAction.AddBinding("<Mouse>/leftButton");
        anyKeyAction.AddBinding("<Mouse>/rightButton");
        anyKeyAction.AddBinding("<Mouse>/middleButton");
        anyKeyAction.AddBinding("<Touchscreen>/primaryTouch/press");
        anyKeyAction.AddBinding("<Gamepad>/buttonSouth");
        anyKeyAction.AddBinding("<Gamepad>/buttonNorth");
        anyKeyAction.AddBinding("<Gamepad>/buttonEast");
        anyKeyAction.AddBinding("<Gamepad>/buttonWest");
        anyKeyAction.Enable();
    }

    private static void CreateAxisAction(string name, params string[] bindings)
    {
        var action = new InputAction(name, InputActionType.Value);
        foreach (var binding in bindings) action.AddBinding(binding);
        action.Enable();
        axisActions[name] = action;
    }

    private static void SetupTextHandling()
    {
        if (currentKeyboard != null)
        {
            currentKeyboard.onTextInput -= OnTextInput;
            currentKeyboard.onTextInput += OnTextInput;
        }
        // InputSystem.onIMECompositionChange는 존재하지 않으므로 제거합니다.
        // IME 처리는 onTextInput을 통해 간접적으로 지원됩니다.
        Application.onBeforeRender -= FrameReset;
        Application.onBeforeRender += FrameReset;
    }

    #endregion

    #region 공개 API

    // UIInput 호환성 API
    public static string inputString => inputBuffer.ToString();
    public static IMECompositionMode imeCompositionMode { get; set; }
    public static bool imeIsSelected => !string.IsNullOrEmpty(compositionString);

    public static bool GetKeyDown(KeyCode key) => GetKeyState(key, KeyState.Down);
    public static bool GetKeyUp(KeyCode key) => GetKeyState(key, KeyState.Up);
    public static bool GetKey(KeyCode key) => GetKeyState(key, KeyState.Held);

    public static bool GetKeyDown(string name)
    {
        if (TryParseKeyCode(name, out var key))
        {
            return GetKeyDown(key);
        }
        return false;
    }

    public static bool GetKeyUp(string name)
    {
        if (TryParseKeyCode(name, out var key))
        {
            return GetKeyUp(key);
        }
        return false;
    }

    public static bool GetKey(string name)
    {
        if (TryParseKeyCode(name, out var key))
        {
            return GetKey(key);
        }
        return false;
    }

    public static float GetAxis(string axisName)
    {
        if (string.IsNullOrEmpty(axisName) || !axisActions.TryGetValue(axisName, out var action) || action == null || !action.enabled)
            return 0f;

        if (action.expectedControlType == "Vector2")
        {
            var value = action.ReadValue<Vector2>();
            if (axisName.EndsWith("X") || axisName == "Horizontal") return value.x;
            if (axisName.EndsWith("Y") || axisName == "Vertical") return value.y;
            return value.magnitude;
        }
        return action.ReadValue<float>();
    }

    public static float GetAxisRaw(string axisName)
    {
        if (string.IsNullOrEmpty(axisName) || !axisActions.TryGetValue(axisName, out var action) || action == null || !action.enabled)
            return 0f;

        if (action.expectedControlType == "Vector2")
        {
            var value = action.ReadValue<Vector2>();
            if (axisName.EndsWith("X") || axisName == "Horizontal") return value.x;
            if (axisName.EndsWith("Y") || axisName == "Vertical") return value.y;
            return value.magnitude;
        }
        var rawValue = action.ReadValue<float>();
        return Mathf.Abs(rawValue) > 0 ? Mathf.Sign(rawValue) : 0;
    }

    public static bool GetButton(string buttonName) => GetButtonState(buttonName, KeyState.Held);
    public static bool GetButtonDown(string buttonName) => GetButtonState(buttonName, KeyState.Down);
    public static bool GetButtonUp(string buttonName) => GetButtonState(buttonName, KeyState.Up);

    public static bool GetMouseButton(int button) => GetKey(KeyCode.Mouse0 + button);
    public static bool GetMouseButtonDown(int button) => GetKeyDown(KeyCode.Mouse0 + button);
    public static bool GetMouseButtonUp(int button) => GetKeyUp(KeyCode.Mouse0 + button);

    public static Vector3 mousePosition => currentMouse?.position.ReadValue() ?? Vector3.zero;
    public static Vector2 mouseScrollDelta => currentMouse?.scroll.ReadValue() ?? Vector2.zero;
    public static bool mousePresent => currentMouse != null;

    public static bool anyKey => anyKeyAction?.IsPressed() ?? false;
    public static bool anyKeyDown => anyKeyAction?.triggered ?? false;

    public static bool touchSupported => currentTouchscreen != null;
    public static bool stylusTouchSupported => Pen.current != null;
    public static bool touchPressureSupported => currentTouchscreen?.pressure.IsActuated() ?? false;
    public static bool multiTouchEnabled { get; set; } = true;
    public static bool simulateMouseWithTouches { get; set; } = true;
    public static int touchCount
    {
        get
        {
            UpdateCachedTouches();
            return cachedTouches.Count;
        }
    }

    public static Touch GetTouch(int index)
    {
        UpdateCachedTouches();
        if (index < 0 || index >= cachedTouches.Count)
            throw new ArgumentOutOfRangeException(nameof(index), $"Touch index {index} is out of range. Count: {cachedTouches.Count}");
        return cachedTouches[index];
    }

    public static Touch[] touches
    {
        get
        {
            UpdateCachedTouches();
            return cachedTouches.ToArray();
        }
    }

    public static Vector3 acceleration
    {
        get
        {
            if (currentAccelerometer != null && currentAccelerometer.enabled)
            {
                return currentAccelerometer.acceleration.ReadValue();
            }
            return Vector3.zero;
        }
    }

    public static int accelerationEventCount
    {
        get
        {
            UpdateAccelerationEvents();
            return accelerationEventBuffer.Count;
        }
    }

    public static AccelerationEvent[] accelerationEvents
    {
        get
        {
            UpdateAccelerationEvents();
            return accelerationEventBuffer.ToArray();
        }
    }

    public static AccelerationEvent GetAccelerationEvent(int index)
    {
        UpdateAccelerationEvents();
        if (index < 0 || index >= accelerationEventBuffer.Count)
            throw new ArgumentOutOfRangeException(nameof(index));
        return accelerationEventBuffer[index];
    }

    public static Gyroscope gyro
    {
        get
        {
            if (_gyroscope == null && currentGyro != null)
            {
                _gyroscope = new Gyroscope(currentGyro);
            }
            return _gyroscope;
        }
    }
    public static bool isGyroAvailable => currentGyro != null && currentGyro.enabled;
    public static DeviceOrientation deviceOrientation
    {
        get
        {
            switch (Screen.orientation)
            {
                case ScreenOrientation.Portrait:
                    return DeviceOrientation.Portrait;
                case ScreenOrientation.PortraitUpsideDown:
                    return DeviceOrientation.PortraitUpsideDown;
                case ScreenOrientation.LandscapeLeft:
                    return DeviceOrientation.LandscapeLeft;
                case ScreenOrientation.LandscapeRight:
                    return DeviceOrientation.LandscapeRight;
                default:
                    return DeviceOrientation.Unknown;
            }
        }
    }

    /// <summary>
    /// 안드로이드에서 뒤로가기 버튼이 앱을 종료할지 여부를 결정합니다.
    /// New Input System에서는 이 기능을 직접 제어할 수 없으므로, 이 속성은 동작하지 않습니다.
    /// </summary>
    public static bool backButtonLeavesApp { get; set; } = true;

    public static string[] GetJoystickNames()
    {
        return Gamepad.all.Select(g => g.displayName).ToArray();
    }

    public static void ResetInputAxes()
    {
        foreach (var action in axisActions.Values)
        {
            // New Input System에서는 직접 리셋하는 기능 대신,
            // 다음 프레임에 값이 0으로 돌아가는 것을 기대합니다.
            // 필요 시, action.Disable() / action.Enable()을 고려할 수 있습니다.
        }
        LogInfo("Input axes reset requested.");
    }

    #endregion

    #region 내부 로직

    private static void OnTextInput(char character) => inputBuffer.Append(character);
    private static void FrameReset()
    {
        inputBuffer.Clear();
        accelerationEventBuffer.Clear();
    }

    private static bool TryParseKeyCode(string name, out KeyCode key)
    {
        try
        {
            key = (KeyCode)Enum.Parse(typeof(KeyCode), name, true);
            return true;
        }
        catch (ArgumentException)
        {
            LogError($"'{name}'은(는) 유효한 KeyCode가 아닙니다.");
            key = KeyCode.None;
            return false;
        }
    }

    private static bool GetButtonState(string buttonName, KeyState state)
    {
        if (TryParseKeyCode(buttonName, out var key))
        {
            return GetKeyState(key, state);
        }

        LogError($"'{buttonName}'은(는) 유효한 KeyCode 이름이 아닙니다.");
        return false;
    }

    private static bool GetKeyState(KeyCode key, KeyState state)
    {
        try
        {
            // 마우스 버튼 처리
            if (key >= KeyCode.Mouse0 && key <= KeyCode.Mouse6)
            {
                if (currentMouse == null) return false;
                var button = (key - KeyCode.Mouse0) switch
                {
                    0 => currentMouse.leftButton,
                    1 => currentMouse.rightButton,
                    2 => currentMouse.middleButton,
                    _ => null
                };
                return button != null && CheckButtonState(button, state);
            }

            // 조이스틱 버튼 처리
            if (key >= KeyCode.JoystickButton0 && key <= KeyCode.JoystickButton19)
            {
                if (currentGamepad == null) return false;
                int buttonIndex = (int)key - (int)KeyCode.JoystickButton0;
                if (currentGamepad.allControls.Count <= buttonIndex) return false;
                var button = currentGamepad.allControls[buttonIndex] as ButtonControl;
                return button != null && CheckButtonState(button, state);
            }

            // 키보드 키 처리
            if (currentKeyboard != null && keyMapping.TryGetValue(key, out var mappedKey) && mappedKey != Key.None)
            {
                return CheckButtonState(currentKeyboard[mappedKey], state);
            }
        }
        catch (Exception ex)
        {
            LogError($"GetKeyState 오류 (Key: {key}, State: {state}): {ex.Message}");
        }
        return false;
    }

    private static bool CheckButtonState(ButtonControl button, KeyState state)
    {
        return state switch
        {
            KeyState.Down => button.wasPressedThisFrame,
            KeyState.Up => button.wasReleasedThisFrame,
            KeyState.Held => button.isPressed,
            _ => false
        };
    }

    private static void UpdateCachedTouches()
    {
        if (Time.unscaledTime == lastTouchUpdateTime) return;
        lastTouchUpdateTime = Time.unscaledTime;
        cachedTouches.Clear();

#if UNITY_EDITOR || UNITY_STANDALONE
        if (simulateMouseWithTouches) SimulateMouseAsTouch();
#else
        if (currentTouchscreen == null) return;

        foreach (var activeTouch in UnityEngine.InputSystem.EnhancedTouch.Touch.activeTouches)
        {
            cachedTouches.Add(ConvertEnhancedTouch(activeTouch));
            if (!multiTouchEnabled) break;
        }
#endif
    }

    private static void UpdateAccelerationEvents()
    {
        if (currentAccelerometer != null && currentAccelerometer.enabled)
        {
            var accel = currentAccelerometer.acceleration.ReadValue();
            accelerationEventBuffer.Add(new AccelerationEvent { acceleration = accel, deltaTime = Time.deltaTime });
        }
    }

    private static Touch ConvertEnhancedTouch(UnityEngine.InputSystem.EnhancedTouch.Touch enhancedTouch)
    {
        var legacyPhase = enhancedTouch.phase switch
        {
            InputSystemTouchPhase.Began => LegacyTouchPhase.Began,
            InputSystemTouchPhase.Moved => LegacyTouchPhase.Moved,
            InputSystemTouchPhase.Stationary => LegacyTouchPhase.Stationary,
            InputSystemTouchPhase.Ended => LegacyTouchPhase.Ended,
            InputSystemTouchPhase.Canceled => LegacyTouchPhase.Canceled,
            _ => LegacyTouchPhase.Canceled
        };

        return new Touch
        {
            fingerId = enhancedTouch.finger.index,
            position = enhancedTouch.screenPosition,
            deltaPosition = legacyPhase == LegacyTouchPhase.Began ? Vector2.zero : enhancedTouch.delta,
            deltaTime = Time.unscaledDeltaTime,
            tapCount = enhancedTouch.tapCount,
            phase = legacyPhase
        };
    }

    private static void SimulateMouseAsTouch()
    {
        if (currentMouse == null) return;

        const int mouseFingerId = 0;
        var phase = LegacyTouchPhase.Canceled;
        bool addToList = false;

        if (currentMouse.leftButton.wasPressedThisFrame)
        {
            phase = LegacyTouchPhase.Began;
            addToList = true;
        }
        else if (currentMouse.leftButton.wasReleasedThisFrame)
        {
            phase = LegacyTouchPhase.Ended;
            addToList = true;
        }
        else if (currentMouse.leftButton.isPressed)
        {
            phase = currentMouse.delta.ReadValue().sqrMagnitude < 0.1f ? LegacyTouchPhase.Stationary : LegacyTouchPhase.Moved;
            addToList = true;
        }

        if (addToList)
        {
            cachedTouches.Add(new Touch
            {
                fingerId = mouseFingerId,
                position = currentMouse.position.ReadValue(),
                deltaPosition = phase == LegacyTouchPhase.Began ? Vector2.zero : currentMouse.delta.ReadValue(),
                deltaTime = Time.unscaledDeltaTime,
                tapCount = 1,
                phase = phase
            });
        }
    }

    #endregion

    #region 유틸리티 및 헬퍼 클래스

    [Conditional("UNITY_EDITOR"), Conditional("DEVELOPMENT_BUILD")]
    private static void LogInfo(string message)
    {
        if (enableDebugLogs) Debug.Log($"[Input(Legacy Input Wrapper)] {message}");
    }

    [Conditional("UNITY_EDITOR"), Conditional("DEVELOPMENT_BUILD")]
    private static void LogError(string message)
    {
        if (enableDebugLogs) Debug.LogError($"[Input(Legacy Input Wrapper)] {message}");
    }

    public static void SetDebugLogging(bool enabled) => enableDebugLogs = enabled;

    #endregion
}

 

 

[참고]

강제로 Wrapping 하지 않고, 필요한 부분만 하고싶다면,

위 Input.cs를 NewInput.cs로 클래스/파일 명을 변경하고
적용하고 싶은 스크립트 상단에 using Input = NewInput; 을 써서 해당 스크립트만 부분적으로 적용해도 될 것 같다.

반응형

'Unity3D > Script' 카테고리의 다른 글

Define Invoker  (0) 2025.08.21
[Unity] 캔디 랩(Candy Wrap) 현상 보정 방법  (0) 2025.02.27
[펌] OcclusionCulling2D  (0) 2024.03.14
[펌] GameQualitySettings  (0) 2024.01.19
[최적화] Automatic quality settings  (0) 2024.01.19
Posted by blueasa
, |

[링크] https://www.postype.com/@fail-blanc/post/17808791

 

5. 릴툰 쉐이더의 수치와 기능에 대해 알아보자!: 잡동사니

VRChat에서 사용되는 쉐이더에는 여러 종류가 있습니다… UTS라고 불리는 유니티 쨩 툰 쉐이더도 있고, poiyomi toon shader도 있습니다. 하지만 가장 대중적으로 많이 사용되는 건 누가 뭐래도 lilToon이

www.postype.com

 

반응형
Posted by blueasa
, |

[링크] https://github.com/lilxyzw/lilToon

 

GitHub - lilxyzw/lilToon: Feature-rich shaders for avatars

Feature-rich shaders for avatars. Contribute to lilxyzw/lilToon development by creating an account on GitHub.

github.com

 

반응형
Posted by blueasa
, |

 

[링크]

[Unity] New Input System #1 | 세팅하기

 

[Unity] New Input System #1 | 세팅하기

/* 수업 들으며 했던 내용들을 실습해보며 정리한 글입니다. 잘못된 부분이 있다면 말씀해주세요. */ 게임은 특정 플랫폼 하나만을 겨냥하고 개발하는 경우는 거의 없다. 스케일이 커지게 되면 PC

daekyoulibrary.tistory.com

 

[Unity] New Input System #2 | 키 입력을 스크립트에 전달하여 처리하기

 

[Unity] New Input System #2 | 키 입력을 스크립트에 전달하여 처리하기

저번 글에서 기본적인 셋팅을 끝냈었다. 이번 글에서는 입력값을 스크립트(script)에 전달하는 방식 중 2가지 정도를 사용하여 로직을 제어하는 걸 해보고자 한다. Player 게임 오브젝트에 Player Input

daekyoulibrary.tistory.com

 

반응형
Posted by blueasa
, |

[링크] https://gamecoke.tistory.com/entry/Unity-New-Input-System

 

Unity New Input System

New Input System은 기존의 Input Manager의 문제점인플랫폼 간의 키 연동 문제를 개선하기 위해 나온 툴입니다. 또한, 위 사진처럼 키 매핑을 보기 쉽게 만들어서 개발자가 한눈에 어떤 키가 있는지 파

gamecoke.tistory.com

 

반응형
Posted by blueasa
, |

[링크] https://github.com/Nickk888SAMP/InputSystemManager

 

GitHub - Nickk888SAMP/InputSystemManager: A Input System Manager for Unity allowing to get data from the Input System directly f

A Input System Manager for Unity allowing to get data from the Input System directly from a Singleton! - Nickk888SAMP/InputSystemManager

github.com

 

반응형
Posted by blueasa
, |

[링크] https://devfrog.tistory.com/11

 

Anti Cheat Toolkit

Unity Asset Store에서 구매 가능한 유료 치트 방지 플러그인이다. 크게 ObscuredTypes, ObscuredPrefs, ObscuredFile, ObscuredFilePrefs 4가지로 나눠져있다. 하나 씩 예제 코드와 함께 살펴보자. ObscuredTypes Cheat Engine,

devfrog.tistory.com

 

반응형
Posted by blueasa
, |