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

카테고리

분류 전체보기 (2794)
Unity3D (852)
Script (91)
Extensions (16)
Effect (3)
NGUI (81)
UGUI (9)
Physics (2)
Shader (37)
Math (1)
Design Pattern (2)
Xml (1)
Tips (201)
Link (23)
World (1)
AssetBundle (25)
Mecanim (2)
Plugins (78)
Trouble Shooting (70)
Encrypt (7)
LightMap (4)
Shadow (4)
Editor (12)
Crash Report (3)
Utility (9)
UnityVS (2)
Facebook SDK (2)
iTween (3)
Font (13)
Ad (14)
Photon (2)
IAP (1)
Google (8)
Android (51)
iOS (44)
Programming (478)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (11)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
using UnityEngine;

public class OcclusionCulling2D : MonoBehaviour
{
    [System.Serializable] public class ObjectSettings
    {
        [HideInInspector] public string title;
        public GameObject theGameObject;

        public Vector2 size = Vector2.one;
        public Vector2 offset = Vector2.zero;
        public bool multiplySizeByTransformScale = true;

        public Vector2 sized { get; set; }
        public Vector2 center { get; set; }
        public Vector2 TopRight { get; set; }
        public Vector2 TopLeft { get; set; }
        public Vector2 BottomLeft { get; set; }
        public Vector2 BottomRight { get; set; }
        public float right { get; set; }
        public float left { get; set; }
        public float top { get; set; }
        public float bottom { get; set; }

        public Color DrawColor = Color.white;
        public bool showBorders = true;
    }

    public ObjectSettings[] objectSettings = new ObjectSettings[1];

    private Camera camera;
    private float cameraHalfWidth;

    public float updateRateInSeconds = 0.1f;

    private float timer;

    void Awake(){ 
        camera = GetComponent<Camera>();
        cameraHalfWidth = camera.orthographicSize * ((float)Screen.width / (float)Screen.height);

        foreach(ObjectSettings o in objectSettings){
           o.sized = o.size * (o.multiplySizeByTransformScale ? new Vector2(Mathf.Abs(o.theGameObject.transform.localScale.x), Mathf.Abs(o.theGameObject.transform.localScale.y)) : Vector2.one);
            o.center = (Vector2)o.theGameObject.transform.position + o.offset;

            o.TopRight = new Vector2(o.center.x + o.sized.x, o.center.y + o.sized.y);
            o.TopLeft = new Vector2(o.center.x - o.sized.x, o.center.y + o.sized.y);
            o.BottomLeft = new Vector2(o.center.x - o.sized.x, o.center.y - o.sized.y);
            o.BottomRight = new Vector2(o.center.x + o.sized.x, o.center.y - o.sized.y);

            o.right = o.center.x + o.sized.x;
            o.left = o.center.x - o.sized.x;
            o.top = o.center.y + o.sized.y;
            o.bottom = o.center.y - o.sized.y;
        }
    }

    void OnDrawGizmosSelected()
    {
        foreach(ObjectSettings o in objectSettings)
        {
            if(o.theGameObject)
            {
                o.title = o.theGameObject.name;

                if(o.showBorders)
                {
                    o.TopRight = new Vector2(o.center.x + o.sized.x, o.center.y + o.sized.y);
                    o.TopLeft = new Vector2(o.center.x - o.sized.x, o.center.y + o.sized.y);
                    o.BottomLeft = new Vector2(o.center.x - o.sized.x, o.center.y - o.sized.y);
                    o.BottomRight = new Vector2(o.center.x + o.sized.x, o.center.y - o.sized.y);
                    Gizmos.color = o.DrawColor;
                    Gizmos.DrawLine(o.TopRight, o.TopLeft);
                    Gizmos.DrawLine(o.TopLeft, o.BottomLeft);
                    Gizmos.DrawLine(o.BottomLeft, o.BottomRight);
                    Gizmos.DrawLine(o.BottomRight, o.TopRight);
                }
            }
        }
    }
    
    void FixedUpdate()
    {
        timer += Time.deltaTime;
        if(timer > updateRateInSeconds) timer = 0;
        else return;

        float cameraRight = camera.transform.position.x + cameraHalfWidth;
        float cameraLeft = camera.transform.position.x - cameraHalfWidth;
        float cameraTop = camera.transform.position.y + camera.orthographicSize;
        float cameraBottom = camera.transform.position.y - camera.orthographicSize;

        foreach(ObjectSettings o in objectSettings)
        {
            if(o.theGameObject)
            {
                bool IsObjectVisibleInCastingCamera = o.right > cameraLeft & o.left < cameraRight & // check horizontal
                                                      o.top > cameraBottom & o.bottom < cameraTop; // check vertical
                o.theGameObject.SetActive(IsObjectVisibleInCastingCamera);
            }
        }
    }
}

[출처] https://www.youtube.com/watch?v=hbBDqdoHUpE

반응형
Posted by blueasa
, |
// jave.lin 2022.03.17

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

// jave.lin : 输出系统的信息工具类
public class DumpSystemInfoUtil
{
    // jave.lin : 通过反射得方式获取不了
    public static string DumpSystemInfoByReflection()
    {
        var type = typeof(SystemInfo);
        // jave.lin : 下面发现反射不成功
        var fields = type.GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
        var fieldsToStrList = new List<string>();
        foreach (var field in fields)
        {
            // 过滤 过期得 API
            var obsoAttris = field.GetCustomAttributes(typeof(ObsoleteAttribute), true);
            if (obsoAttris != null && obsoAttris.Length > 0) continue;
            fieldsToStrList.Add(field.Name + ":" + field.GetValue(null).ToString());
        }
        return string.Join("\n", fieldsToStrList.ToArray());
    }

    // jave.lin : 所以只能通过一个个得去输出
    public static string DumpSystemInfoByManualyPrint()
    {
        var list = new List<string>(
            new string[]{
                "SystemInfo:\n",
                "\tbatteryLevel:" + SystemInfo.batteryLevel,
                "\tbatteryStatus:" + SystemInfo.batteryStatus,
                "\toperatingSystem:" + SystemInfo.operatingSystem,
                "\toperatingSystemFamily:" + SystemInfo.operatingSystemFamily,
                "\tprocessorType:" + SystemInfo.processorType,
                "\tprocessorFrequency:" + SystemInfo.processorFrequency,
                "\tprocessorCount:" + SystemInfo.processorCount,
                "\tsystemMemorySize:" + SystemInfo.systemMemorySize,
                "\tdeviceUniqueIdentifier:" + SystemInfo.deviceUniqueIdentifier,
                "\tdeviceName:" + SystemInfo.deviceName,
                "\tdeviceModel:" + SystemInfo.deviceModel,
                "\tsupportsAccelerometer:" + SystemInfo.supportsAccelerometer,
                "\tsupportsGyroscope:" + SystemInfo.supportsGyroscope,
                "\tsupportsLocationService:" + SystemInfo.supportsLocationService,
                "\tsupportsVibration:" + SystemInfo.supportsVibration,
                "\tsupportsAudio:" + SystemInfo.supportsAudio,
                "\tdeviceType:" + SystemInfo.deviceType,
                "\tgraphicsMemorySize:" + SystemInfo.graphicsMemorySize,
                "\tgraphicsDeviceName:" + SystemInfo.graphicsDeviceName,
                "\tgraphicsDeviceVendor:" + SystemInfo.graphicsDeviceVendor,
                "\tgraphicsDeviceID:" + SystemInfo.graphicsDeviceID,
                "\tgraphicsDeviceVendorID:" + SystemInfo.graphicsDeviceVendorID,
                "\tgraphicsDeviceType:" + SystemInfo.graphicsDeviceType,
                "\tgraphicsUVStartsAtTop:" + SystemInfo.graphicsUVStartsAtTop,
                "\tgraphicsDeviceVersion:" + SystemInfo.graphicsDeviceVersion,
                "\tgraphicsShaderLevel:" + SystemInfo.graphicsShaderLevel,
                "\tgraphicsMultiThreaded:" + SystemInfo.graphicsMultiThreaded,
                "\tsupportsShadows:" + SystemInfo.supportsShadows,
                "\tsupportsRawShadowDepthSampling:" + SystemInfo.supportsRawShadowDepthSampling,
                "\tsupportsMotionVectors:" + SystemInfo.supportsMotionVectors,
                "\tsupports3DTextures:" + SystemInfo.supports3DTextures,
                "\tsupports2DArrayTextures:" + SystemInfo.supports2DArrayTextures,
                "\tsupports3DRenderTextures:" + SystemInfo.supports3DRenderTextures,
                "\tsupportsCubemapArrayTextures:" + SystemInfo.supportsCubemapArrayTextures,
                "\tcopyTextureSupport:" + SystemInfo.copyTextureSupport,
                "\tsupportsComputeShaders:" + SystemInfo.supportsComputeShaders,
                "\tsupportsInstancing:" + SystemInfo.supportsInstancing,
                "\tsupportsHardwareQuadTopology:" + SystemInfo.supportsHardwareQuadTopology,
                "\tsupports32bitsIndexBuffer:" + SystemInfo.supports32bitsIndexBuffer,
                "\tsupportsSparseTextures:" + SystemInfo.supportsSparseTextures,
                "\tsupportedRenderTargetCount:" + SystemInfo.supportedRenderTargetCount,
                "\tsupportsMultisampledTextures:" + SystemInfo.supportsMultisampledTextures,
                "\tsupportsMultisampleAutoResolve:" + SystemInfo.supportsMultisampleAutoResolve,
                "\tsupportsTextureWrapMirrorOnce:" + SystemInfo.supportsTextureWrapMirrorOnce,
                "\tusesReversedZBuffer:" + SystemInfo.usesReversedZBuffer,
                "\tnpotSupport:" + SystemInfo.npotSupport,
                "\tmaxTextureSize:" + SystemInfo.maxTextureSize,
                "\tmaxCubemapSize:" + SystemInfo.maxCubemapSize,
                "\tsupportsAsyncCompute:" + SystemInfo.supportsAsyncCompute,
                "\tsupportsAsyncGPUReadback:" + SystemInfo.supportsAsyncGPUReadback,
                "\tsupportsMipStreaming:" + SystemInfo.supportsMipStreaming,
            });
        return string.Join("\n", list.ToArray());
    }
}

// jave.lin : 设备定档级别枚举
public enum eDeviceLevel
{
    Unknow = -1,
    VeryLow = 0,
    Low,
    Middle,
    High,
}

// jave.lin : 画质级别
public enum eQualityLevel
{
    Low = 1,
    Middle = 2,
    High = 3,
    Ultra = 4,
}

// jave.lin : shader lod
public enum eShaderLOD
{
    //High = 800,
    //Middle = 400,
    //Low = 200,
    //VeryLow = 100,
    //UnLimit = -1,
    // jave.lin : 太低的值对 built-in shader 的影响太大
    High = 800,
    Middle = 600,
    Low = 400,
    VeryLow = 200,
    UnLimit = -1,
}

// jave.lin : 游戏的质量设置类
public class GameQualitySettings
{
    private const string QS_POWER_SAVE_MODE_KEY = "graphics_setting.power_save_mode";
    private const string QS_QUALITY_LEVEL_KEY = "graphics_setting.quality_level";

    // 当 品质有调整事出发的事件函数
    public static Action<eQualityLevel> onLevelChanged;

    // 源来的 AA 和 阴影设置
    private static int srcAntiAliasing;
    private static ShadowQuality srcShadows;

    // 当前 品质等级
    private static eQualityLevel curLevel;

    // 获取 设备定档的质量级别
    public static eQualityLevel DeviceAdapterLevel
    {
        get; private set;
    }

    // 获取 或 设置 公开给外部的画质设置的属性
    public static eQualityLevel GraphicsLevel
    {
        get { return curLevel; }
        set
        {
            if (curLevel != value)
            {
                curLevel = value;
                _SetCurLevel(value);
                PlayerPrefs.SetInt(QS_QUALITY_LEVEL_KEY, (int)value);
                if (null != onLevelChanged)
                {
                    onLevelChanged.Invoke(value);
                }
            }
        }
    }

    // 获取 或 设置 省电模式, true: 30FPS, false: 60FPS
    public static bool PowerSaveMode
    {
        get
        {
            return Application.targetFrameRate < 40;
        }

        set
        {
            var src_v = PlayerPrefs.GetInt(QS_POWER_SAVE_MODE_KEY, -1);
            var tar_v = value ? 1 : 0;
            if (src_v != tar_v)
            {
                PlayerPrefs.SetInt(QS_POWER_SAVE_MODE_KEY, tar_v);
            }
            Application.targetFrameRate = value ? 30 : 60;
        }
    }

    // 静态构造函数
    static GameQualitySettings()
    {
        // 备份 原始 AA 和 阴影
        srcAntiAliasing = QualitySettings.antiAliasing;
        srcShadows = QualitySettings.shadows;

        // 初始化 品质 和 省电模式
        _SetDefaultPowerSaveMode();
        _SetDefaultLevel();
    }

    // 设置默认的品质等级
    private static void _SetDefaultLevel()
    {
        // 先 分析 并 设置 设备默认品质等级
        DeviceAdapterLevel = _AnalysicDeviceLevel();

        var src_v = PlayerPrefs.GetInt(QS_QUALITY_LEVEL_KEY, -1);
        // 如果品质等级没有设置过
        if (src_v == -1)
        {
            // 那么使用 设备默认品质
            PlayerPrefs.SetInt(QS_QUALITY_LEVEL_KEY, (int)DeviceAdapterLevel);
            curLevel = GraphicsLevel;
        }
        // 如果品质等级有设置过
        else
        {
            curLevel = (eQualityLevel)src_v;
        }
    }

    // 设置默认的省电模式
    private static void _SetDefaultPowerSaveMode()
    {
        var src_v = PlayerPrefs.GetInt(QS_POWER_SAVE_MODE_KEY, 0);
        if (src_v == 0)
        {
            PowerSaveMode = true;
            PlayerPrefs.SetInt(QS_POWER_SAVE_MODE_KEY, 1);
        }
        else
        {
            PowerSaveMode = src_v == 1;
        }
    }

    // 分析设备所属默认的品质等级
    private static eQualityLevel _AnalysicDeviceLevel()
    {
        if (SystemInfo.processorFrequency >= 2500 &&
            SystemInfo.processorCount >= 8 &&
            SystemInfo.systemMemorySize >= (6 * 1024) &&
            SystemInfo.graphicsMemorySize >= (2 * 1024) &&
            SystemInfo.graphicsShaderLevel >= 30 &&
            SystemInfo.graphicsMultiThreaded &&
            SystemInfo.supportsShadows &&
            SystemInfo.supportsInstancing &&
            SystemInfo.supports32bitsIndexBuffer
            )
        {
            return eQualityLevel.Ultra;
        }
        else if (SystemInfo.processorFrequency >= 2000 &&
            SystemInfo.processorCount >= 4 &&
            SystemInfo.systemMemorySize >= (4 * 1024) &&
            SystemInfo.graphicsMemorySize >= (1 * 1024) &&
            SystemInfo.graphicsShaderLevel >= 20
            )
        {
            return eQualityLevel.High;
        }
        else if (SystemInfo.processorFrequency >= 1500 &&
            SystemInfo.processorCount >= 2 &&
            SystemInfo.systemMemorySize >= (2 * 1024) &&
            SystemInfo.graphicsMemorySize >= (512) &&
            SystemInfo.graphicsShaderLevel >= 10
            )
        {
            return eQualityLevel.Middle;
        }
        else
        {
            return eQualityLevel.Low;
        }
    }

    // 设置 当前品质等级
    private static void _SetCurLevel(eQualityLevel level)
    {
        _SetAntiAliasing(level);
        _SetResolution(level);
        _SetTexMipmapOffset(level);
        _SetShadow(level);
        _SetLODBias(level);
        _SetGraphicsTier(level);
        _SetShaderLOD(level);
        _SetGlobalShaderKW(level);
    }

    // 设置 AA
    private static void _SetAntiAliasing(eQualityLevel level)
    {
        if (level >= eQualityLevel.High)
        {
            QualitySettings.antiAliasing = srcAntiAliasing;
        }
        else
        {
            QualitySettings.antiAliasing = 0;
        }
    }

    // 设置分辨率
    private static void _SetResolution(eQualityLevel level)
    {
        // jave.lin : BRP(Built-In Rendering Pipeline) 中
        // 需要对应的 Camera 开启 AllowDynamicResolution 后才能生效
        switch (level)
        {
            case eQualityLevel.Low:
                QualitySettings.resolutionScalingFixedDPIFactor = 0.75f;
                break;
            case eQualityLevel.Middle:
                QualitySettings.resolutionScalingFixedDPIFactor = 0.85f;
                break;
            case eQualityLevel.High:
                QualitySettings.resolutionScalingFixedDPIFactor = 0.85f;
                break;
            case eQualityLevel.Ultra:
                QualitySettings.resolutionScalingFixedDPIFactor = 1.00f;
                break;
        }
    }

    // 设置 Tex 纹理 mipmap offset
    private static void _SetTexMipmapOffset(eQualityLevel level)
    {
        switch (level)
        {
            case eQualityLevel.Low:
                QualitySettings.masterTextureLimit = DeviceAdapterLevel < eQualityLevel.High ? 3 : 2;
                break;
            case eQualityLevel.Middle:
                QualitySettings.masterTextureLimit = DeviceAdapterLevel < eQualityLevel.High ? 2 : 1;
                break;
            case eQualityLevel.High:
                QualitySettings.masterTextureLimit = 0;
                break;
            case eQualityLevel.Ultra:
                QualitySettings.masterTextureLimit = 0;
                break;
        }
    }

    // 设置阴影
    private static void _SetShadow(eQualityLevel level)
    {
        switch (level)
        {
            case eQualityLevel.Low:
            case eQualityLevel.Middle:
                //QualitySettings.shadows = ShadowQuality.Disable; // jave.lin : 有 BUG,会导致,Animator 组件中的 culling mode 不是 always animated 的对象超出屏幕的画,会被自动停止掉
                // 所以下面使用 shadowDistance 来替代关闭
                QualitySettings.shadowDistance = 0;
                break;
            case eQualityLevel.High:
                QualitySettings.shadows = srcShadows;
                QualitySettings.shadowResolution = ShadowResolution.Low;
                QualitySettings.shadowDistance = 70;
                break;
            case eQualityLevel.Ultra:
                QualitySettings.shadows = srcShadows;
                QualitySettings.shadowResolution = ShadowResolution.High;
                QualitySettings.shadowDistance = 100;
                break;
        }
    }

    // 设置 LOD 偏移
    private static void _SetLODBias(eQualityLevel level)
    {
        switch (level)
        {
            case eQualityLevel.Low:
                QualitySettings.lodBias = 0.5f;
                break;
            case eQualityLevel.Middle:
                QualitySettings.lodBias = 0.75f;
                break;
            case eQualityLevel.High:
            case eQualityLevel.Ultra:
                QualitySettings.lodBias = 1.0f;
                break;
        }
    }

    // 设置 GraphicsTier 的层级
    private static void _SetGraphicsTier(eQualityLevel level)
    {
        switch (level)
        {
            case eQualityLevel.Low:
            case eQualityLevel.Middle:
                Graphics.activeTier = GraphicsTier.Tier1;
                break;
            case eQualityLevel.High:
                Graphics.activeTier = GraphicsTier.Tier2;
                break;
            case eQualityLevel.Ultra:
                Graphics.activeTier = GraphicsTier.Tier3;
                break;
        }
    }

    // 设置 Shader LOD
    private static void _SetShaderLOD(eQualityLevel level)
    {
        switch (level)
        {
            case eQualityLevel.Low:
                Shader.globalMaximumLOD = (int)eShaderLOD.VeryLow;
                break;
            case eQualityLevel.Middle:
                Shader.globalMaximumLOD = (int)eShaderLOD.Low;
                break;
            case eQualityLevel.High:
                Shader.globalMaximumLOD = (int)eShaderLOD.Middle;
                break;
            case eQualityLevel.Ultra:
                Shader.globalMaximumLOD = (int)eShaderLOD.High;
                break;
            default:
                Shader.globalMaximumLOD = (int)eShaderLOD.UnLimit;
                break;
        }
    }

    // 设置全局Shader Keyword
    private static void _SetGlobalShaderKW(eQualityLevel level)
    {
        switch (level)
        {
            case eQualityLevel.Low:
            case eQualityLevel.Middle:
                Shader.DisableKeyword("_SOFT_PARTICLE_ON");
                break;
            case eQualityLevel.High:
            case eQualityLevel.Ultra:
                Shader.EnableKeyword("_SOFT_PARTICLE_ON");
                break;
        }
    }
}

// jave.lin : 后效基类
public class PPBasic : MonoBehaviour { }
// jave.lin : Bloom 后效
public class BloomPP : PPBasic
{
    // jave.lin : start 时 先处理,处理当前品质
    // 然后监听 品质变化的时间
    private void Start()
    {
        OnQualityLevelChanged(GameQualitySettings.GraphicsLevel);
        GameQualitySettings.onLevelChanged -= OnQualityLevelChanged;
        GameQualitySettings.onLevelChanged += OnQualityLevelChanged;
    }
    // jave.lin : 销毁时记得删除回调
    private void OnDestroy()
    {
        GameQualitySettings.onLevelChanged -= OnQualityLevelChanged;
    }

    private void OnQualityLevelChanged(eQualityLevel ql)
    {
        // jave.lin : 当 品质等级大于或等于高时,才开启 Bloom 后效
        enabled = ql >= eQualityLevel.High;
    }
}

 

 

[출처] https://blog.csdn.net/linjf520/article/details/123546253

 

Unity - 画质设置_unity systeminfo.processorfrequency 值大于5000mhz-CSDN博客

Show me Your Code, Talk Is Cheap. 以前自己写的类,现在重新写一份 代码 便于日后直接搬运使用,代码都是相当简单,都是直接调用 unity 的 API 设置即可,可以理解为就是搬运而已 环境 Unity : 2018.2.11f1

blog.csdn.net

 

반응형
Posted by blueasa
, |

Unity 2021.3.33f1

----

 

앱 최초 실행 시, 최적화를 위해 그래픽 품질 관련 자동 설정하는 기능을 찾아보다 적용하고 올려 둠.

 

참조한 글을 보니 기본적으로 유니티 SystemInfo에서 지원하는 하드웨어 스펙을 보고 판단해서 Quality Level을 메기고 있다.

해당 레벨은 Unity의 UnityEngine.QualityLevel을 참조하고 있어서 대응 하는 enum을 추가(eQualityLevel)하고,

60프레임을 사용하기 위해 해당 부분 값만 수정했다.

 

필요할 때 AutoChooseQualityLevel()을 호출하고, 반환 받은 eQualityLevel 값에 따라 원하는 설정을 적용해주면 된다.

/// <summary>
/// UnityEngine.QualityLevel에 대응하는 enum
/// </summary>
public enum eQualityLevel : int 
{
    Fastest,
    Fast,
    Simple,
    Good,       // [참고] PC(i7-10700/64GB/RTX3700)에서 Good 나옴
    Beautiful,
    Fantastic
}

private eQualityLevel AutoChooseQualityLevel()
{
    Debug.Assert(Enum.GetNames(typeof(QualityLevel)).Length == Enum.GetNames(typeof(eQualityLevel)).Length, "Please update eQualityLevel to the new quality levels.");

	var shaderLevel = SystemInfo.graphicsShaderLevel;
    var cpus = SystemInfo.processorCount;
    var vram = SystemInfo.graphicsMemorySize;
    var fillrate = 0;

    // ShaderLevel
    if (shaderLevel < 10)
    	fillrate = 1000;
    else if (shaderLevel < 20)
    	fillrate = 1300;
    else if (shaderLevel < 30)
    	fillrate = 2000;
    else
    	fillrate = 3000;
    // CPU Count
    if (6 <= cpus)
    	fillrate *= 3;
    else if (3 <= cpus)
    	fillrate *= 2;
    // VRam
    if (512 <= vram)
    	fillrate *= 2;
    else if (vram <= 128)
    	fillrate /= 2;
        
    var resx = Screen.width;
    var resy = Screen.height;
    var target_fps = 60.0f;		// 현재 게임의 타겟 프레임에 맞게 설정
    var fillneed = (resx * resy + 400f * 300f) * (target_fps / 1000000.0f);
    // Change the values in levelmult to match the relative fill rate
    // requirements for each quality level.
    var levelmult = new float[] { 5.0f, 30.0f, 80.0f, 130.0f, 200.0f, 320.0f };

    const int max_quality = (int)eQualityLevel.Fantastic;
    var level = 0;
    while (level < max_quality && fillneed * levelmult[level + 1] < fillrate)
        ++level;

    var quality = (eQualityLevel)level;
    Debug.Log(string.Format("{0}x{1} need {2} has {3} = {4} level", resx, resy, fillneed, fillrate, quality.ToString()));

    return quality;
}

 

 

P.s. 참조 글 중에 에셋도 적혀있긴한데 사서 써보진 않음.

 

 

[참조] https://stackoverflow.com/questions/20978106/automatic-quality-settings/20978462#20978462

 

Automatic quality settings

a lot of apps that I see nowadays(Android) instead of having an options screen to select the graphical level, it just automatically does it, I know this is kind of a vast question, but how can I do...

stackoverflow.com

 

[참조] https://discussions.unity.com/t/auto-detect-quality-settings/24351

 

auto detect quality settings

is there a way that unity can auto detect client GPU/CPU capability and performance to set best quality settings?

discussions.unity.com

 

[에셋] https://assetstore.unity.com/packages/tools/integration/auto-quality-hardware-assessment-tool-aqhat-74134

 

Auto Quality Hardware Assessment Tool - AQHAT | 기능 통합 | Unity Asset Store

Use the Auto Quality Hardware Assessment Tool - AQHAT from Eager Amoeba® on your next project. Find this integration tool & more on the Unity Asset Store.

assetstore.unity.com

 

반응형
Posted by blueasa
, |

유니티 (Unity)에서 멀티 키 딕셔너리 (Multi Key Dictionary)를 사용하는 방법에 대해 검색 결과를 제공해드리겠습니다.

멀티 키 딕셔너리는 두 개 이상의 키로 값을 저장하고 검색할 수 있는 자료구조입니다. 유니티에서는 기본적으로 이러한 기능을 제공하지 않지만, 다음과 같은 방법을 사용하여 멀티 키 딕셔너리를 구현할 수 있습니다:

Tuple을 사용한 멀티 키 딕셔너리 구현: Tuple을 이용하여 여러 키를 하나의 키로 묶고, 해당 키에 대한 값을 딕셔너리에 저장하는 방식입니다. 이렇게 하면 여러 키로 값을 조회하거나 저장할 수 있습니다.

using System;
using System.Collections.Generic;

public class MultiKeyDictionary<TKeyTuple, TValue>
{
    private Dictionary<TKeyTuple, TValue> dictionary = new Dictionary<TKeyTuple, TValue>();

    public void Add(TKeyTuple keys, TValue value)
    {
        dictionary[keys] = value;
    }

    public bool TryGetValue(TKeyTuple keys, out TValue value)
    {
        return dictionary.TryGetValue(keys, out value);
    }
}

// 사용 예시
var multiKeyDict = new MultiKeyDictionary<(int, string), int>();
multiKeyDict.Add((1, "key1"), 100);
multiKeyDict.Add((2, "key2"), 200);

if (multiKeyDict.TryGetValue((1, "key1"), out int result))
{
    Debug.Log("Value found: " + result);
}


C# 9의 init-only 프로퍼티와 튜플 사용: C# 9부터 init-only 프로퍼티를 사용하여 딕셔너리의 값을 읽기 전용으로 설정하고, 튜플을 활용하여 멀티 키를 표현할 수 있습니다.

using System;
using System.Collections.Generic;

public class MultiKeyDictionary<TValue>
{
    private Dictionary<(int, string), TValue> dictionary = new Dictionary<(int, string), TValue>();

    public void Add(int key1, string key2, TValue value)
    {
        dictionary[(key1, key2)] = value;
    }

    public IReadOnlyDictionary<(int, string), TValue> Dictionary => dictionary;
}

// 사용 예시
var multiKeyDict = new MultiKeyDictionary<int>();
multiKeyDict.Add(1, "key1", 100);
multiKeyDict.Add(2, "key2", 200);

if (multiKeyDict.Dictionary.TryGetValue((1, "key1"), out int result))
{
    Debug.Log("Value found: " + result);
}

이렇게 유니티에서 멀티 키 딕셔너리를 구현할 수 있습니다. 코드는 예시일 뿐이며, 실제 프로젝트에서 적절한 방식으로 적용하셔야 합니다.

 

[출처] ChatGPT

 

[참조] https://stackoverflow.com/questions/1171812/multi-key-dictionary-in-c

 

Multi-key dictionary in c#?

I know there isn't one in the BCL but can anyone point me to a good opensource one? By Multi I mean 2 keys. ;-)

stackoverflow.com

 

 

반응형
Posted by blueasa
, |

[수정] Rename 방식으로 변경(2024-07-04]

----

 

[빌드 시, Builtin AssetBundles에서 해당 Platform 에셋번들만 빌드 되도록 하기 위한 PreProcessor 추가]

기본적으로 에셋번들은 플랫폼 별로 모두 가지고 있는데 해당 플랫폼에 맞는 에셋번들만 빌드 되도록 하기 위해서

빌드 전(Preprocessor) 해당 안되는 AssetBundle을 Hidden 폴더(예:.Android or .iOS)로 Rename 했다가, 빌드 후(Postprocessor) 되돌려 놓음.

 

using System;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using System.IO;
#if UNITY_2018_1_OR_NEWER
using UnityEditor.Build.Reporting;
#endif
using UnityEditor.Callbacks;

namespace blueasa
{
    /// <summary>
    /// 빌드 시, Built-in AssetBundle에서 해당 Platform AssetBundle만 빌드 되도록 하기 위한 Preprocessor
    /// 빌드 전(Preprocessor) 해당 안되는 AssetBundle을 Hidden 폴더(예:.Android or .iOS)로 Rename 했다가, 빌드 후(Postprocessor) 되돌려 놓음
    /// [Hidden(./~) 참조] https://docs.unity3d.com/kr/2021.3/Manual/SpecialFolders.html
    /// </summary>
#if UNITY_2018_1_OR_NEWER
    public class BuildPreprocessor_BuiltinAssetBundle : IPreprocessBuildWithReport, IPostprocessBuildWithReport
#else
    public class BuildPreprocessor_BuiltinAssetBundle : IPreprocessBuild, IPostprocessBuild
#endif
    {
        private static readonly string m_strAndroid = "Android";
        private static readonly string m_strDotAndroid = ".Android";
        private static readonly string m_striOS = "iOS";
        private static readonly string m_strDotiOS = ".iOS";
        private static readonly string m_strDotMeta = ".meta";
        private static readonly string m_strAssetBundles = "AssetBundles";

        private static readonly string m_strDataPath = Application.dataPath;
        private static readonly string m_strStreamingAssetsPath = Application.streamingAssetsPath;
        
        private static readonly string m_strAssetBundlesPath_Builtin_FullPath = string.Format("{0}/{1}", m_strStreamingAssetsPath, m_strAssetBundles);
        private static readonly string m_strAssetBundlesPath_Builtin_Android_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_strAndroid);
        private static readonly string m_strAssetBundlesPath_Builtin_DotAndroid_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_strDotAndroid);
        private static readonly string m_strAssetBundlesPath_Builtin_iOS_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_striOS);
        private static readonly string m_strAssetBundlesPath_Builtin_DotiOS_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_strDotiOS);

        public int callbackOrder { get { return 0; } }

        private static void Refresh()
        {
            System.Threading.Thread.Sleep(100);
            AssetDatabase.Refresh();
            System.Threading.Thread.Sleep(100);
        }

#if UNITY_2018_1_OR_NEWER
        public void OnPreprocessBuild(BuildReport report)
#else
        public void OnPreprocessBuild(BuildTarget target, string path)
#endif
        {
            Debug.LogWarning($"[OnPreprocessBuild] {this}");

            // 빌드 전, 다른 플랫폼 AssetBundle 폴더 임시 제외
#if UNITY_ANDROID
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_iOS_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_DotiOS_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename 'iOS' Folder to '.iOS'");
                Directory.Move(m_strAssetBundlesPath_Builtin_iOS_FullPath, m_strAssetBundlesPath_Builtin_DotiOS_FullPath);
                // 빈(Empty) 폴더 생성을 방지하기 위해서 폴더의 meta 파일도 함께 삭제
                if (true == File.Exists(m_strAssetBundlesPath_Builtin_iOS_FullPath + m_strDotMeta))
                {
                    File.Delete(m_strAssetBundlesPath_Builtin_iOS_FullPath + m_strDotMeta);
                }
                Refresh();
            }
#elif UNITY_IOS
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_Android_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_DotAndroid_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename 'Android' Folder to '.Android'");
                Directory.Move(m_strAssetBundlesPath_Builtin_Android_FullPath, m_strAssetBundlesPath_Builtin_DotAndroid_FullPath);
                // 빈(Empty) 폴더 생성을 방지하기 위해서 폴더의 meta 파일도 함께 삭제
                if (true == File.Exists(m_strAssetBundlesPath_Builtin_Android_FullPath + m_strDotMeta))
                {
                    File.Delete(m_strAssetBundlesPath_Builtin_Android_FullPath + m_strDotMeta);
                }
                Refresh();
            }
#endif
            // Start listening for errors when build starts
            Application.logMessageReceived += OnBuildError;
        }

        // CALLED DURING BUILD TO CHECK FOR ERRORS
        private void OnBuildError(string condition, string stacktrace, LogType type)
        {
            Debug.LogWarning($"[OnBuildError] {condition} {stacktrace} {type}");

            if (type == LogType.Error)
            {
                // FAILED TO BUILD, STOP LISTENING FOR ERRORS
                Application.logMessageReceived -= OnBuildError;

                // 빌드 에러 시에도 이동된 파일 되돌리기
                RestoreTemporarilyMovedAssetbundles();
            }
        }

#if UNITY_2018_1_OR_NEWER
        public void OnPostprocessBuild(BuildReport report)
#else
        public void OnPostprocessBuild(BuildTarget target, string path)
#endif
        {
            Debug.LogWarning($"[OnPostprocessBuild] {this}");

            // [빌드 후] 제외 됐던 다른 플랫폼 AssetBundle 폴더 되돌리기
            RestoreTemporarilyMovedAssetbundles();

            // IF BUILD FINISHED AND SUCCEEDED, STOP LOOKING FOR ERRORS
            Application.logMessageReceived -= OnBuildError;
        }

        public static void RestoreTemporarilyMovedAssetbundles()
        {
            Debug.LogWarning($"[RestoreTemporarilyMovedAssetbundles]");

            // [빌드 후] 제외 됐던 다른 플랫폼 AssetBundle 폴더 되돌리기
#if UNITY_ANDROID
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_DotiOS_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_iOS_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename '.iOS' Folder to 'iOS'");
                Directory.Move(m_strAssetBundlesPath_Builtin_DotiOS_FullPath, m_strAssetBundlesPath_Builtin_iOS_FullPath);
                Refresh();
            }
#elif UNITY_IOS
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_DotAndroid_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_Android_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename '.Android' Folder to 'Android'");
                Directory.Move(m_strAssetBundlesPath_Builtin_DotAndroid_FullPath, m_strAssetBundlesPath_Builtin_Android_FullPath);
                Refresh();
            }
#endif
        }
    }
}

 

 

반응형
Posted by blueasa
, |

[링크] https://rito15.github.io/posts/unity-editor-define-symbol/

 

유니티 - Scripting Define Symbol 스크립트로 제어하기

Scripting Define Symbol?

rito15.github.io

 

반응형
Posted by blueasa
, |
public class CoroutineHelper : MonoBehaviour
{
    private static MonoBehaviour monoInstance;
    
    [RuntimeInitializeOnLoadMethod]
    private static void Initializer()
    {
        monoInstance = new GameObject($"[{nameof(CoroutineHelper)}]").AddComponent<CoroutineHelper>();
        DontDestroyOnLoad(monoInstance.gameObject);
    }
    
    public new static Coroutine StartCoroutine(IEnumerator coroutine)
    {
        return monoInstance.StartCoroutine(coroutine);
    }

    public new static void StopCoroutine(Coroutine coroutine)
    {
        monoInstance.StopCoroutine(coroutine);
    }
}

 

 

사용법 :

CoroutineHelper.StartCoroutine(코루틴_함수());

CoroutineHelper.StopCoroutine(코루틴);

 

그냥 유니티 생명주기를 가진 한 싱글톤 객체에 코루틴 실행을 몰아주는 방식

가끔 MonoBehaviour 상속 안 받고 코루틴 실행하고 싶을 때가 있는데 그럴때 사용할 수 있을듯..

 

예전에 unirx(MainThreadDispatcher)한 번 사용해본적 있었는데 거기서 아이디어 얻음

 

[출처] https://gall.dcinside.com/mgallery/board/view/?id=game_dev&no=66595

 

유니티 MonoBehaviour 상속 안 받고 코루틴 사용하기 - 인디 게임 개발 마이너 갤러리

public class CoroutineHelper : MonoBehaviour{ private static MonoBehaviour monoInstance; [RuntimeInitializeOnLo

gall.dcinside.com

 

반응형
Posted by blueasa
, |

[링크] https://codingcoding.tistory.com/584

 

Well512 알고리즘 예제, 난수 생성기, 랜덤 포레스트 (Random Forest)

Well512 알고리즘 예제, 난수 생성기, 랜덤 포레스트 (Random Forest) 주요 참조 사이트 : 표준 rand()함수보다 유용한 난수 생성기 알고리즘 – MT, WELL512 [링크] 소스 코드 - WindowsFormsApplication1.zip..

codingcoding.tistory.com

 

반응형
Posted by blueasa
, |

[링크1] https://www.sysnet.pe.kr/2/0/1264

[링크2] https://111110.tistory.com/entry/WELL-512-%EB%9E%9C%EB%8D%A4%EC%83%81%EC%88%98-%EC%83%9D%EC%84%B1-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98

[링크3] https://github.com/Mokbii/CSharpSyncRandom/tree/master/SyncRandomTest

 

반응형
Posted by blueasa
, |

이번에는 HTTP 서버에 데이터 요청을 보내고 데이터를 얻어오기, 혹은 데이터를 보내는 방법입니다.


목차

1. WWW 클래스로 구현(권장하지 않습니다.)

2-1. Get 방식

2-2. Post 방식

2. UnityWebRequest 클래스로 구현(권장)

2-1. Get 방식

2-2. Post 방식

3. 요약 및 정리


1. WWW 클래스로 구현

1-1 GET 방식

아래는 제가 작성한 간단한 GET 방식의 코드입니다.

GET 방식 테스트를 위해 이곳을 이용하겠습니다.

https://developers.neople.co.kr/contents/apiDocs

 

Neople Developers

## 참고 사항 >- 타임라인 코드 다중 입력 시 콤마(,)를 이용해서 구분 처리   ex) /timeline?code=101,102,103 - startDate, endDate 요청 변수 사용 예시   ex) /timeline?startDate=20180901T0000&endDate=20180930T2359           /timeline?startDate=2018-09-01 00:00&endDate=2018-09-30 23:59   ※ 기간 검색

developers.neople.co.kr

 

APIKey는 로그인을 하시고 발급받으시면 보입니다.

응답 요청은 코루틴을 이용해서 보내도록 하겠습니다.

코루틴을 이용하는 이유는 응답을 보내고 오는 시간이 걸리기 때문입니다.

다른 곳에서 작성하시면 응답이 오기 전에 처리하게 되어 오류가 출력됩니다.

위에 코드는 예시 코드입니다.

홈페이지에서 발급받은 apikey를 저기에 입력하시면 됩니다.

url을 보시면 servers? apikey=하고 요청을 보내게 되는데

헤더 부분에 정보를 넣어서 보내는 곳이 저곳입니다.

코드에 주석을 넣었으니 읽으면서 확인해보세요.

코드를 실행해보시면 이렇게 응답이 옵니다.

1-2 POST 방식

위에는 POST 방식입니다.

POST 방식은 따로 동작을 처리해야 하기 때문에 개인적으로 만든 서버를 이용하여 설명하겠습니다.(저건 사설 ip 주소이기 때문에 똑같이 적으셔도 동작 안 합니다.)

(테스트해보고 싶으신 분들은 따로 서버를 만드셔서 실험해보세요)

POST 방식은? apikey= 이런 게 없습니다.

필드명에 apikey를 넣고 데이터를 넣으시면 됩니다.

필요한 내용은 주석 처리했습니다.

서버에서 로그인 정보가 맞으면 응답을 보냅니다.


2. unitywebrequest 클래스

이제 www 클래스는 Unity에서 사용을 권장하지 않습니다.

사용하시면 주의로 unitywebrequest를 사용하라고 나올 텐데요.

이것을 사용하는 방법을 알려드리겠습니다.

UnityEngine.Networking을 추가해줍니다.

2-1 GET 방식

아래는 WWW 클래스를 UnityWebRequest 클래스 방식으로 수정한 것입니다.

수정한 부분은 빨간 줄로 밑줄을 쳤습니다.

2-2 POST 방식

수정한 부분은 밑줄 쳤습니다.


3. 요약 및 정리

정리 : 통신방식에는 GET과 POST가 있다.

GET은 쉬우나 보안 취약, 용량 제한

POST는 어려우나 보안에 좋고, 용량 제한이 없다.

WWW 클래스는 이제 사용하지 않는다.

UnityWepRequest를 사용하자.(설명은 WWW 클래스에 적어놓았다.)

 

 

[출처] mungto.tistory.com/190

 

www 클래스, UnityWebRequest 클래스 사용하기(HTTP 서버에 데이터 가져오기, 보내기 등)

이번에는 HTTP 서버에 데이터 요청을 보내고 데이터를 얻어오기, 혹은 데이터를 보내는 방법입니다. 목차 1. WWW 클래스로 구현(권장하지 않습니다.) 2-1. Get 방식 2-2. Post 방식 2. UnityWebRequest 클래스

mungto.tistory.com

 

[참조] docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.Post.html

반응형
Posted by blueasa
, |