반응형

텍스쳐 임포트시 모바일 프로젝트에서는 기본적으로 Advanced로 사용하고 mipmap을 끄는게 통상적인 방식입니다.

하지만, 유니티 기본셋팅은 항상 Texture 타입으로 설정되어 mipmap도 무조건 켜지게 됩니다.

아래 코드를 편집하여 프로젝트에 넣어주시면 원하시는 셋팅값으로 텍스쳐 임포트를 설정할수 있습니다.


using UnityEngine;

using UnityEditor;


public class TexturePostProcessor : AssetPostprocessor

{

void OnPostprocessTexture(Texture2D texture)

{

TextureImporter importer = assetImporter as TextureImporter;

importer.textureType = TextureImporterType.Advanced;   // 기본 타입을 Advanced로..

importer.anisoLevel = 1;

importer.filterMode = FilterMode.Bilinear;

importer.mipmapEnabled = false;  // 기본 mipmap 해제

// 필요한 설정값을 이곳에 넣어주시면 됩니다.


Object asset = AssetDatabase.LoadAssetAtPath(importer.assetPath, typeof(Texture2D));

if (asset)

{

EditorUtility.SetDirty(asset);

}

else

{

texture.anisoLevel = 1;

texture.filterMode = FilterMode.Bilinear;          

}

}

반응형

반응형

Prefab_Skin = (GameObject)EditorGUILayout.ObjectField("_Skin Object", Prefab_Skin, typeof(GameObject));

라고 코딩을 하자 아래와 같은 에러 로그가 발생했다.

 

warning CS0618: `UnityEditor.EditorGUILayout.ObjectField(string, UnityEngine.Object, System.Type, params UnityEngine.GUILayoutOption[])' is obsolete: `Check the docs for the usage of the new parameter 'allowSceneObjects'.'

 

에러 로그가 발생하지 않으려면 마지막에 Bool 값을 넣어서 아래와 같이 입력해줘야 에러가 발생하지 않는다.

 

_prefabObj = (GameObject)EditorGUILayout.ObjectField("Target Prefab", _prefabObj, typeof(GameObject), true);

 

 

반응형
반응형

UnityEngineInternal.APIUpdaterRuntimeServices를 사용하면 아래와 같은 Warning Log 가 발생한다.

 

warning CS0618: `UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(UnityEngine.GameObject, string, string)' is obsolete: `AddComponent(string) has been deprecated. Use GameObject.AddComponent<T>() / GameObject.AddComponent(Type) instead.

 

이 로그가 발생하지 않도록 하기 위해서는 툴을 거쳐서 스크립트를 집어넣도록 설정했던것을 해당 스크립트로 바로 불러오도록 값을 수정해 주면 에러로그가 발생하지 않는다.

 

예)

string ParticleStrScript = "ParticleCtrl";

 

UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent (PrefabBipProp, "Assets/Scripts/Editor/Art/CHAR_Prefab_FbxChanger.cs", ParticleStrScript); 

 

위 코드를 사용하면 warning이 발생하게 되며 아래와 같이 수정해주면 로그가 사라진다.

 

-->  PrefabBip.AddComponent (typeof(ParticleCtrl));

 

 

반응형
반응형

이번에 기존에 등록된 스크립트를 제거하고 새로운 스크립트를 등록하는 툴을 유니티 스크립트로 제작하게 됐는데 이번 툴을 제작하면서 C#에서의 이름 찾기와 재귀 함수 사용법에 대해서 알게 됐습니다.

 

아래 스크립트는 기존 있던  Animated_Death 컴포넌트를 삭제하고 Animated_Death_Combine 이라는 새로운 컴포넌트를 추가하면서 해당 컴포넌트에 하위 오브젝트들의 이름을 뒤져 필요한 Transform을 이름으로 검색하여 자동으로 넣어주는 툴입니다.

아래 스크립트를 참고하면 여러가지 응용이 가능할거 같습니다.

 

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

public class AnimateDeathChange : EditorWindow
{
    GameObject Target = null;
    GameObject pObj = null;

    string strScript = "Animated_Death_Combine";

    string DelScript = "Animated_Death";

    [MenuItem("RK Tools/Art/AnimateDeathChange")]

    //윈도우 생성
    static void InitWindow () 
    {
        AnimateDeathChange window = (AnimateDeathChange)EditorWindow.GetWindow(typeof(AnimateDeathChange));
        window.autoRepaintOnSceneChange = true;

        if (window == null)
        {
            Debug.Log("error: cannot find Dummy");
        }
    }

    //오브젝트 선택창
    void OnGUI()
    { 
        SelectTargetObject ();
        ScriptSetting ();
    }


    //원본소스 선택창
    void SelectTargetObject()
    {
        GUILayout.Space(16f);
        GUI.contentColor = Color.white;
        EditorGUILayout.LabelField("해당 모델에 죽기 스크립트를 추가해 줍니다.");
        EditorGUILayout.LabelField("각각의 항목을 채워주세요.");

        EditorGUILayout.BeginVertical("box");
        Target = (GameObject)EditorGUILayout.ObjectField("Base Model (Project)", Target, typeof(GameObject), false);
        strScript = EditorGUILayout.TextField ( "Add Script Name", strScript);
        DelScript = EditorGUILayout.TextField ( "Del Script Name", DelScript);
        EditorGUILayout.EndVertical();
    }

    //복사 오브젝트는 하이어라키 창에서 실시간 교체 가능
    void OnSelectionChange()
    {        
        if (Selection.gameObjects == null || Selection.gameObjects.Length < 1) {
            Focus();
            Target = null;
            return;
        }

        if (Target != Selection.gameObjects[0])
        {
            Target = Selection.gameObjects[0];
            Focus();           
        }
        else
            Target = Selection.gameObjects[0];
    }

    //실행시 기존 스크     
    void ScriptSetting()
    {
        GUILayout.Space(16f);
        if (GUILayout.Button ("Set Script To Object")) 
        {

            // 새로운 컴포넌트를 넣어주는 명령어 
            UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(Target, "Assets/Scripts/Editor/Art/AnimateDeathChange.cs", strScript);

            SerchObjectsInChild(Target.transform);

            // 기존 컴포넌트를 제거해주는 명령어
            GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());
            GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());
            GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());




            if (null == Target)
                return;
            if (null == pObj)
                return;
            else 
            {
                SerchObjectsInChild(Target.transform);
            }


        }
    }
    //
    //    //원본 오브젝트의 하위 더미를 찾는 재귀 함수
    private void SerchObjectsInChild(Transform tr)
    {
        int iCount = tr.childCount;

        for (int i = 0; i < iCount; ++i) {
            Transform tempTr = tr.GetChild (i);
//            Debug.Log (tempTr);

            // Contains 명령어는 문장을 뒤져 해당 텍스트를 찾는 명령어이다.

            if (tempTr.name.Contains ("_body")) {                
                Target.GetComponent<Animated_Death_Combine>().Model_1 = tempTr.gameObject.GetComponent<Renderer>();
            } 
            else if (tempTr.name.Contains ("_head")|| tempTr.name.Contains ("face")) {
                Target.GetComponent<Animated_Death_Combine>().Model_2 = tempTr.gameObject.GetComponent<Renderer>();
            } 
            else if (tempTr.name.Contains ("_weap")) {                
                Target.GetComponent<Animated_Death_Combine>().Model_3 = tempTr.gameObject.GetComponent<Renderer>();
            }
            else if (tempTr.name.Contains ("_BODY")) {
                Target.GetComponent<Animated_Death_Combine>().Shadow_1 = tempTr.gameObject.GetComponent<Renderer>();
            } else if (tempTr.name.Contains ("_HEAD") || tempTr.name.Contains ("_FACE")) {                
                Target.GetComponent<Animated_Death_Combine>().Shadow_2 = tempTr.gameObject.GetComponent<Renderer>();
            }else if (tempTr.name.Contains ("_WEAP")) {                
                Target.GetComponent<Animated_Death_Combine>().Shadow_3 = tempTr.gameObject.GetComponent<Renderer>();
            }

            if (tempTr.childCount > 0)
            {
                SerchObjectsInChild(tempTr);
            }
        }
    }
}

반응형
반응형
GameObject.DestroyImmediate (Target.GetComponentInChildren<Animated_Death>());

 

위 구문은 선택된 타겟 오브젝트의 자식을 뒤져 Animated_Death 라는 컴포넌트를 제거해 주는 명령어 입니다.

GameObject.DestroyImmediate (Target.GetComponent<Animated_Death>());  라고 쓰면 자식이 아닌 본인의 컴포넌트를 제거해 주는 거겠죠~

 

이번에 죽기 관련 스크립트를 수정하면서 알게 된 내용이라 메모해둡니다~

반응형
반응형

유니티 스크립트에서는 메터리얼에 담겨 있는 텍스쳐의 다양한 정보를 편집해서 사용할 수 있다.

MainTexture는 maintexture / mainTextureOffset / mainTextureScale  등을 활용하여 크기나 좌표를 편집할 수 있고,

SubTexture들은 String 값을 참조하여 편집할 수 있다.

아래의 코드를 참고하자.

 

m_Renderer.material.mainTextureOffset    = new Vector2(offset.x - ((int)offset.x), offset.y - ((int)offset.y));

m_Renderer.material.SetTextureOffset("_Alpha", offset)//_Alpha 에 담겨 있는 텍스쳐의 좌표를 편집할 수 있다.

m_Renderer.material.mainTextureScale    = m_size;

m_Renderer.material.SetTextureScale("_Alpha", m_size);

반응형
반응형

유니티의 타임 스케일을 사용할 경우 파티클 애니메이션이 터지는 문제가 종종 발생한다.

이 문제를 해결하기 위해 Time.unscaledDeltaTime 함수를 사용하면 문제를 해결할 수 있다.

타임 스케일을 무시하고 출력되도록 하는 함수다.

 

 

public class UnscaledTimeParticle : MonoBehaviour

{

 // Update is called once per frame

    void Update()

{

if (Time.timeScale < 0.01f)

{

particleSystem.Simulate(Time.unscaledDeltaTime, true, false);

}

}

}

반응형
반응형

프로젝트를 진행하면서 파티클 빌트인 셰이더들이 문제를 일으켜 일괄 변환해 줘야 하는 상황이 발생했습니다.

일일이 바꾸는 번거러움을 덜고자 제작한 스크립트 입니다.

 

 

1. 모바일 엔진 셰이더(바뀌어야할 셰이더 선택)

2. 바뀔 셰이더 선택

3. 대상 메터리얼들 선택(폴더 / 파일)

4. 대상 메터리얼중 1번과 동일 셰이더를 찾아 2번으로 바꿔줌~ 일괄 교체~

 

 

 

 

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class MaterialControl : EditorWindow
{  
    public Shader shader1 = null;
    public Shader shader2 = null;

    public Color defaultColor;
    public Color tintColor = new Color(0.5F, 0.5F, 0.5F, 0.5F);

    Shader defaultShader = null;
    Shader changeShader = null;

    static float UIWidth = 500;
    static string OutputPath = string.Empty;
    
    bool IsError = false;
    List<string> tempPathList = new List<string>();
    List<string> InputPaths = new List<string>();
    
    string SearchPattern = "*.mat;*.*";


//    ShaderImporter texFormatSelected = ShaderImporter.GetAtPath;
//    TextureImporterFormat texFormatToChange = TextureImporterFormat.ETC2_RGBA8;

    [MenuItem("RK Tools/Art/MaterialControl")]
    static void InitWindow()
    {
        MaterialControl window = EditorWindow.GetWindow<MaterialControl>();
        window.position = new Rect(100f, 100f, UIWidth, 400f); //  초기 사이즈
        
        window.title = "Save Bundle";
        window.Show();        
    }
    
    void OnGUI()
    {
        FunctionManual();
        ShaderSetting();
        SelectOutPath();
        ClearSelectFolder();
        OutputSelectedFile();
        ConvertAtlasImage();
    }
    
    void FunctionManual()
    {
        GUILayout.Space(16);
        GUI.contentColor = Color.cyan;
        EditorGUILayout.LabelField("폴더를 선택해서 해당 폴더 내의 Shader를 변경합니다.");
        GUI.contentColor = Color.white;
        GUILayout.Space(8);
    }

    void ShaderSetting()
    {
        defaultShader = (Shader)EditorGUILayout.ObjectField("Original Shader", shader1, typeof(Shader));
        changeShader = (Shader)EditorGUILayout.ObjectField("Change Shader", shader2, typeof(Shader));

        defaultColor = EditorGUILayout.ColorField("Tint Color", tintColor);

        if (defaultShader != null)
        {
            shader1 = defaultShader;
            shader2 = changeShader;
            tintColor = defaultColor;
        }


    }

    string prevPath = Application.dataPath;


    void SelectOutPath()
    {
        if (GUILayout.Button("폴더 or 파일 선택"))
        {            
            GetAssetPath_Project();            
            Repaint();
        }
    }
    
    void GetAssetPath_Project()
    {        
        SelectionProcess();
        
        foreach (string path in tempPathList)
        {
            if (InputPaths.Exists(assetPath => (assetPath == path)))
                continue;
            InputPaths.Add(path);
        }        
        IsError = false;
    }
    
    bool SelectionProcess()
    {
        tempPathList.Clear();
        string AssetPath = string.Empty;
        foreach (UnityEngine.Object obj in Selection.objects)
        {
            AssetPath = AssetDatabase.GetAssetPath(obj);
            if (Directory.Exists(AssetPath))
            {
                tempPathList.AddRange(
                    AssetDatabase.FindAssets("t:Material", new string[] { AssetPath }));
                
                for (int i = 0; i < tempPathList.Count; ++i)
                {
                    tempPathList[i] = AssetDatabase.GUIDToAssetPath(tempPathList[i]);                    
                }
            }
            else if (File.Exists(AssetPath))
            {
                char[] separate = new char[] { ';', '*', '.' };
                foreach (string pattern in SearchPattern.Split(separate))
                {
                    if (AssetPath.ToLower().Contains(pattern))
                    {
                        tempPathList.Add(AssetPath.Replace(Application.dataPath, "Assets"));
                        break;
                    }
                }
            }
        }
        return true;
    }
    
    void ClearSelectFolder()
    {
        if (GUILayout.Button("선택 폴더 or 파일 해제"))
        {
            tempPathList.Clear();
            InputPaths.Clear();
        }
    }
    
    Vector2 scrollPos = new Vector2();
    void OutputSelectedFile()
    {
        EditorGUILayout.LabelField("선택 폴더");
        
        EditorGUILayout.BeginVertical("box", GUILayout.Width(UIWidth));
        scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Width(UIWidth));
        
        foreach (string path in InputPaths)
        {
            EditorGUILayout.LabelField(path);
        }
        EditorGUILayout.EndScrollView();
        EditorGUILayout.EndVertical();
    }
    
    
    void ConvertAtlasImage()
    {        
        EditorGUILayout.BeginHorizontal();

        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginVertical("box", GUILayout.Width(UIWidth));

        if(GUILayout.Button("선택된 메터리얼을 Change Shader로 변환"))
        {
            ChangeShaderToTargetMaterial();
        }
        EditorGUILayout.EndVertical ();
    }


    void ChangeShaderToTargetMaterial()
    {
        foreach (string path in InputPaths)
        {
            AssetDatabase.ImportAsset(path);

            Material material = (Material)AssetDatabase.LoadMainAssetAtPath(path);


            if (material.shader == shader1)
            {
                material.shader = shader2;

                material.SetColor("_TintColor", tintColor);
            }         
        }
    }
}

반응형
반응형

게임을 제작할때 메인 전투씬이 있고 그 곳에 배경씬을 로드되는 식으로 제작될 때가 있습니다.

이때의 문제점이 각각의 배경마다의 포그값이나 후처리 값을 가지고 갈 수 없는 문제점이 있습니다.

 

 

 

 

그럴 때 로드될 배경씬에 포그값 정보에 대한 것을 스크리트로 처리하게 되면 사용이 가능해 집니다.

 

 

1. 스크립트 제작

 

using UnityEngine;
using System.Collections;

public class RK_Fog : MonoBehaviour {

    public bool isFogOnOff = false;

    public Color RkFogColor;

    public enum RkFogMode : int
    {
        Linear = 0,
        Expotential = 1,
        ExpotentialSquared = 2,
    }

    public RkFogMode RkFogModeEnum;

    public float LinearStart = 0.0f;
    public float LinearEnd = 10.0f;

    public float ExpotentialDensity = 0.01f;


    // Use this for initialization
    void Start () {

        if (isFogOnOff == true) {
            RenderSettings.fog = true;
            RenderSettings.fogColor = RkFogColor;

            if ((int)RkFogModeEnum == 0) {
                RenderSettings.fogMode = FogMode.Linear;
                RenderSettings.fogStartDistance = LinearStart;
                RenderSettings.fogEndDistance = LinearEnd;
            }
            if ((int)RkFogModeEnum == 1) {
                RenderSettings.fogMode = FogMode.Exponential;
                RenderSettings.fogDensity = ExpotentialDensity;
            }
            if ((int)RkFogModeEnum == 2) {
                RenderSettings.fogMode = FogMode.ExponentialSquared;
                RenderSettings.fogDensity = ExpotentialDensity;
            }

        } else {
            RenderSettings.fog = false;
        }    
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}

 

 

2. 제작 된 스크립트를 씬에 오브젝트를 만들어 배치합니다.

 

 

 

 

반응형
반응형

카메라에 사용된 후처리(Post Processor / Image Effect)를 키로 제어할 수 있게 되면 다양하게 활용이 가능합니다.

 

캐릭터가 강력한 스킬을 쓸때에 블룸효과를 강하게 준다던지, 컷씬을 제작할때 순간적으로 화면을 어둡게 한다던지.

막타연출에서 모션블러나 라디알 효과등을 원하는 타이밍에 쓰기 위해서 애니메이션으로 후처리를 제어하는 방식이

가장 편하고 좋습니다.

 

또한 씬 전환 트랜지션상황에서도 유용하게 사용이 가능합니다.

 

 

1. 카메라에 다양한 후처리 적용하기

- 먼저 카메라에 사용될 다양한 후처리들을 적용하고, 비활성화 시킵니다.

- 라디알블러, 비네팅, 블룸등을 적용시켰습니다.

 

 

 

2. 스크립트 제작하기

- 카메라에 적용된 후처리들을 파싱하여 사용할 수 있도록 스크립트를 제작합니다.

- 사용된 카메라와 카메라에 사용된 후처리 스크립트를 제어하는 코드입니다.

- Animated_PostProcessor 란 이름으로 제작했습니다.

 

using UnityEngine;
using System.Collections;

public class Animated_PostProcessor : MonoBehaviour
{
    public GameObject PostProcessorCtr;  
    public RK_RadialBlur RadialBlurScript;  
    public bool isRadialBlurScript = false;
    [Range(0.0f, 5.0f)]
    public float RadialBlurStrength = 2.2f;
    [Range(0.0f, 3.0f)]
    public float RadialBlurWidth = 0.5f;


    public RK_ScreenOverlay ScreenOverlayScript
    public bool isScreenOverlayScript = false;
    [Range(0.0f, 1.0f)]
    public float ScreenOverlayintensity = 1.0f;
    public enum OverlayBlendMode2 : int
    {
        Additive = 0,
        ScreenBlend = 1,
        Multiply = 2,
        Overlay = 3,
        AlphaBlend = 4,
    }
    public OverlayBlendMode2 ScreenOverlayblendMode;

    public RK_Vignette VignetteScript;
    public bool isVignetteScript = false;
    [Range(-1.0f, 1.0f)]
    public float Vignetteintensity = 0.25f; 


    public Rk_Bloom BloomScript;
    public bool isBloomScript = false;
    [Range(0.0f, 1.5f)]
    public float BloomScriptthreshold = 0.5f;
    [Range(0.0f, 2.5f)]
    public float BloomScriptintensity = 1.0f;


    void OnEnable ()
    {
        PostProcessorCtr = GameObject.Find("PostProcessorCam");
        RadialBlurScript = PostProcessorCtr.GetComponent<RK_RadialBlur>();
        ScreenOverlayScript = PostProcessorCtr.GetComponent<RK_ScreenOverlay>();
        VignetteScript = PostProcessorCtr.GetComponent<RK_Vignette>();
        BloomScript = PostProcessorCtr.GetComponent<Rk_Bloom>();

        LateUpdate();
    }

    void LateUpdate()
    {
        //라디알 블러 처리
        if (isRadialBlurScript == true) {
            RadialBlurScript.enabled = true;
        } 
        else {
            RadialBlurScript.enabled = false;
        }
        RadialBlurScript.blurStrength = RadialBlurStrength;
        RadialBlurScript.blurWidth = RadialBlurWidth;


        //스크린 오버 레이
        if (isScreenOverlayScript == true) {
            ScreenOverlayScript.enabled = true;
        } 
        else {
            ScreenOverlayScript.enabled = false;
        }
        ScreenOverlayScript.intensity = ScreenOverlayintensity;

        if ((int)ScreenOverlayblendMode == 0) {
            ScreenOverlayScript.blendMode = UnityStandardAssets.ImageEffects.ScreenOverlay.OverlayBlendMode.Additive;
        }
        if ((int)ScreenOverlayblendMode == 1) {
            ScreenOverlayScript.blendMode = UnityStandardAssets.ImageEffects.ScreenOverlay.OverlayBlendMode.ScreenBlend;
        }
        if ((int)ScreenOverlayblendMode == 2) {
            ScreenOverlayScript.blendMode = UnityStandardAssets.ImageEffects.ScreenOverlay.OverlayBlendMode.Multiply;
        }
        if ((int)ScreenOverlayblendMode == 3) {
            ScreenOverlayScript.blendMode = UnityStandardAssets.ImageEffects.ScreenOverlay.OverlayBlendMode.Overlay;
        }
        if ((int)ScreenOverlayblendMode == 4) {
            ScreenOverlayScript.blendMode = UnityStandardAssets.ImageEffects.ScreenOverlay.OverlayBlendMode.AlphaBlend;
        }


        //비네팅 처리
        if (isVignetteScript == true) {
            VignetteScript.enabled = true;
        } 
        else {
            VignetteScript.enabled = false;
        }
        VignetteScript.intensity = Vignetteintensity;


        //블룸 처리
        if (isBloomScript == true) {
            BloomScript.enabled = true;
        } 
        else {
            BloomScript.enabled = false;
        }
        BloomScript.intensity = BloomScriptintensity;
        BloomScript.threshold = BloomScriptthreshold;

    }
}

 

 

 

3. 후처리를 제어할 오브젝트나 파티클에 스크립트와 애니메이션을 붙힙니다.

 

 

 

 

4. 애니메이션으로 후처리 효과들을 제어합니다.

 

 

 

 

5. 완료 테스트 영상

 

 

반응형
반응형

유니티 스크립트에서는 기본적으로 쿼터니언을 제공한다.

스크립트 에디터에서 Quternion을 치고 F12를 누르면~ 

 

쿼터니언에 관련된 다양한 함수를 확인할 수 있다.

 

 

이중 SetLookRotation을 활용하면 편리하게 룩앳 함수를 만들수 있다.

 

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

 [SerializeField] private GameObject targetObj;

 void Start () { 
 }


 void Update () {
  lookTarget(); 
 }

 void lookTarget()
 {
  Quaternion lookAt = Quaternion.identity;    // Querternion 함수 선언
  Vector3 lookatVec = (targetObj.transform.position - transform.position).normalized; //타겟 위치 - 자신 위치 -> 노멀라이즈
  lookAt .SetLookRotation (lookatVec);  // 쿼터니언의 SetLookRotaion 함수 적용
  transform.rotation = lookAt ;   //최종적으로 Quternion  적용
 }
}

위와 같이 코딩을 하고 바라볼 타겟 오브젝트를 지정해 주면 아래처럼 작동하는 룩앳함수가 완성된다.

 

 

 

 

반응형

+ Recent posts