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

public class BionicUnit
{
    public enum BionicUnitType
    {
        Marine,
        Medic
    }
    public BionicUnitType type;
    public string name;
    public BionicUnit(BionicUnitType type, string name)
    {
        this.type = type;
        this.name = name;
    }

}

 

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class DropShip : MonoBehaviour
{
    //마린과 메딕을 관리할 리스트
    public List<BionicUnit> units = new List<BionicUnit>();
    //최대로 태울 수 있는 인원의 수
    public const int CAPACITY = 8;
    void Start()
    {
        //생성자를 만든다
        BionicUnit medic = new BionicUnit(BionicUnit.BionicUnitType.Medic, "메딕");
        BionicUnit marine1 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린1");
        BionicUnit marine2 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린2");
        BionicUnit marine3 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린3");
        BionicUnit marine4 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린4");
        BionicUnit marine5 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린5");
        BionicUnit marine6 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린6");
        BionicUnit marine7 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린7");
        BionicUnit marine8 = new BionicUnit(BionicUnit.BionicUnitType.Marine, "마린8");

        LoadUnit(medic);
        LoadUnit(marine1);
        LoadUnit(marine2);
        LoadUnit(marine3);
        LoadUnit(marine4);
        LoadUnit(marine5);
        LoadUnit(marine6);
        LoadUnit(marine7);
        LoadUnit(marine8);
        UnLoad(medic);
        UnLoadAll();
    }
    public void LoadUnit(BionicUnit unit)
    {
        if (units.Count >= CAPACITY)
        {
            Debug.Log("<color=red> 자리가 없어서 탑승에 실패 </color>");
        }
        else
        {
            units.Add(unit);
            Debug.LogFormat("{0}이 탑승했습니다. ({1}/{2})", unit.type, units.Count, CAPACITY);
            //Debug.LogFormat("{0}이 탑승했습니다. ({1}/{2})", unit.name, units.Count, CAPACITY);
        }
    }
    public void UnLoad(BionicUnit unit)
    {
        this.units.Remove(unit);
        Debug.LogFormat("{0}이 하차했습니다. ({1},{2})", unit.type, units.Count, CAPACITY);
    }
    public void UnLoadAll()
    {
        Debug.Log("모두 하차 시작");
        foreach (BionicUnit unit in units.ToList())
        //()이나 Start() 메서드에서 units 리스트를 수정하지 않도록 합니다. 리스트를 수정할 필요가 있다면 반복문 밖에서 수정
        {
            UnLoad(unit);
        }
    }
}

이런식으로 .ToList()를 쓰지 않으면 오류가 난다.

이유를 찾아보니 멤버변수에 선언한 "원본" 리스트를 변경시키기 때문에 나는 현상이라고 한다.

따라서 해결방법은 list를 새로 만들어서 다시 copy본으로 저장을 하거나

.ToList()를 사용하면된다.

 

자바스크립트에서 문자열을 자를 때 사용하는 splice와 같은 개념인가?

✔ Array.prototype.splice()

 

 

 

 

 

 

만약 unit을 하나씩이 아닌 원하는 수만큼 내리게 하고 싶다면?

int형으로 인자를 하나 더 받으면 된다.

 

'산대특 > 게임 클라이언트 프로그래밍' 카테고리의 다른 글

Quaternion  (1) 2024.03.04
Dodge game  (0) 2024.03.04
코루틴 연습  (0) 2024.03.03
StarCraft - Tank  (0) 2024.02.29
Find the nearest object  (0) 2024.02.27

 

 

표적이 되는 다른 객체들은 assign할 수 만 있도록 MonsterController 스크립트만 할당 한 후

PlayerController에서 MonsterController 인스턴스로 배열을 선언 한후 

각각의 오브젝트를 assign

 

 

https://learn.microsoft.com/ko-kr/dotnet/api/system.collections.generic.list-1?view=net-8.0

 

List<T> 클래스 (System.Collections.Generic)

인덱스로 액세스할 수 있는 강력한 형식의 개체 목록을 나타냅니다. 목록의 검색, 정렬 및 조작에 사용할 수 있는 메서드를 제공합니다.

learn.microsoft.com

 

https://docs.unity3d.com/ScriptReference/Vector3.Distance.html

 

Unity - Scripting API: Vector3.Distance

Success! Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable. Close

docs.unity3d.com

 

코딩을 리뷰해보니 어려운 코드는 없었다.

가장 어려운 부분은 구조를 짜는 것이라 생각하는데

그 중에 인스턴스를 생성하거나 리스트를 생성했을때

값이 어떻게 이동되는지 이름만 옮겨지는지 이름과 값이 함께 쌍으로 넘어가는지 이 부분이 어려웠다.

Debug로 콘솔에 많이 찍어보고 값이 어떻게 출력이 되는지 확인하는데 시간을 가장 많이 썼다.


 

 

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

public class PlayerController : MonoBehaviour
{
    public MonsterController[] monsters;
    void Start()
    {
        List<MonsterController> list = new List<MonsterController>();
        for (int i = 0; i < monsters.Length; i++)
        {
            //몬스터컨트롤러의 인스턴스 생성
            MonsterController controller = monsters[i];
            //Debug.Log(controller);
            //플레이어와의 거리 구하기
            float distance = Vector3.Distance(this.transform.position, controller.transform.position);
            //Debug.LogFormat("이름 : {0}, 거리 : {1}",controller.gameObject.name, distance);
            //Debug.Log(controller.gameObject.name);

            if (distance <= 10)
            {
                list.Add(controller); //객체 형태로 담긴다 
            }
        }
        
        for(int i = 0; i < list.Count; i++)
        {
            MonsterController controller = list[i];
            float distance = Vector3.Distance(this.transform.position, controller.transform.position);
            //Debug.LogFormat("이름 : {0} , 거리 : {1}", controller.gameObject.name, distance);
        }





        //거리가 제일 가까운 객체 찾기
        float minDistance = Mathf.Infinity; // 초기값을 무한대로 지정
        MonsterController minObject = null; // 초기값을 담을 인스턴스 생성

        for(int i = 0; i < list.Count;i++)
        {
            MonsterController controller = list[i];
            float distance = Vector3.Distance(this.transform.position, controller.transform.position);
            if (distance < minDistance)
            {
                minDistance = distance;
                minObject = controller;
            }
        }
        Debug.LogFormat("거리가 가장 가까운 객체 : {0}, 거리 : {1}", minObject.name, minDistance);
    }
}​

 

 

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

public class CSightVisualizer : MonoBehaviour
{
    public float radius = 3;
    private void OnDrawGizmos()
    {
        GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, radius);
    }
}

 

 

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

public class MonsterController : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

'산대특 > 게임 클라이언트 프로그래밍' 카테고리의 다른 글

Quaternion  (1) 2024.03.04
Dodge game  (0) 2024.03.04
코루틴 연습  (0) 2024.03.03
StarCraft - Tank  (0) 2024.02.29
StarCraft - DropShip Logic  (0) 2024.02.28

3D 오브젝트 2pass로 테두리 생성

Shader "Custom/CubeMap"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _CubeMap ("CubeMap", CUBE) = ""{}
    }
    SubShader
    {   
        Tags { "RenderType"="Opaque" }
        //1Pass
        Cull Front
        CGPROGRAM
        #pragma surface surf _NoLight vertex:vert
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void vert(inout appdata_full v){
            v.vertex.xyz = v.vertex.xyz + v.normal.xyz * 0.01;
            }

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb * 0.5;
            o.Alpha = c.a;
        }

        float4 Lighting_NoLight(SurfaceOutput s, float3 lightDir, float atten){
            return float4(0,0,0,1);
            }
        ENDCG

        //2Pass
        CGPROGRAM
        #pragma surface surf Lambert
        #pragma target 3.0

        sampler2D _MainTex;
        samplerCUBE _CubeMap;
        sampler2D _NormalMap;

        struct Input
        {
            float2 uv_MainTex;
            float3 worldRefl; //float2의 uv가아닌 반사벡터를 받는다. => 예약어
            float2 uv_NormalMap;
            INTERNAL_DATA
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            //텍스쳐
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float3 n = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            o.Normal = n;


            float3 ref = WorldReflectionVector(IN, o.Normal);
            float4 d = texCUBE(_CubeMap, ref);
            o.Albedo = c.rgb * 0.5;
            o.Emission = d.rgb * 0.5 ;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

 

_Cube("Cubemap", Cube) = "" {}

// 평소와 다르게 큰따옴표 안을 비워주고 프로퍼티를 생성

 

그리고 Input 구조체안에 float2 UV 아닌 반사 벡터를 받아 온다.

CubeMap에서 알베도를 0으로 선택

CubeMap을 사용할 때 주로 환경 맵이나 반사 맵으로 사용됩니다. CubeMap은 여러 방향으로 카메라를 렌더링하여 캡쳐한 이미지입니다. 이것은 6개의 면으로 구성되어 있으며, 각 면은 카메라가 향하는 방향에 대한 이미지입니다. 이러한 이미지는 일반적으로 장면의 주변 환경을 캡쳐하기 위해 사용됩니다.

알베도 (Albedo)는 표면이 받는 광선의 색상을 나타냅니다. 이것은 표면이 얼마나 빛을 흡수하고 반사하는지를 결정합니다. 일반적으로 알베도는 표면의 기본 색상이며, 이것은 표면이 받는 조명과 반사되는 빛의 양에 영향을 줍니다.

CubeMap을 사용하는 경우, 환경의 주변을 반영하기 위해 표면의 알베도를 0으로 선택하는 것은 일반적으로 의미가 없습니다. 왜냐하면 CubeMap은 주변 환경을 나타내는데 사용되며, 표면의 색상에 해당하는 이미지를 제공하기 때문입니다. 따라서 CubeMap을 사용하는 경우, 표면의 알베도를 0으로 선택하더라도 CubeMap에 의해 환경의 색상이 반영될 것입니다.

그러나 특정 상황에서는 CubeMap의 영향을 제거하고 표면의 색상을 완전히 지정하고자 할 수 있습니다. 이런 경우에는 알베도를 0으로 선택하여 CubeMap의 영향을 제거할 수 있습니다. 이는 표면이 주변 환경에 반영되는 것을 방지하고 표면의 색상을 완전히 제어하고자 할 때 사용될 수 있습니다.                                                                  -feat. GPT

 


Shader error in 'Custom/Reflection': Surface shader Input structure needs  INTERNAL_DATA for this WorldNormalVector or WorldReflectionVector usage at  line 144 (on d3d11)

 

이러한 에러가 발생하는 경우 Input에서 float3 worldRefl이나 float3 worldNormal과  같은 버텍스 월드 노멀에 관련된 데이터를 받아와 surf 함수 내부에서 사용하면서, 동시에  탄젠트 노멀인 UnpackNormal함수를 거친 노멀 데이터를 함수 내부에서 같이 사용하면  에러가 발생 한다.

 

 
 

INTERNAL_DATA

 

접선공간, 표면공, 텍스쳐공간등으로 불리는 Tangent Space 텍스쳐 위에서의 좌표계

공간 고유 원점 좌표계 텍스 좌표 지정되 좌표계.

좌표계에서 U 값이 증가하는 방향을 가리키는 X축과 V 값이 증가하는 방향으로 Y축을 나타낸다

노멀맵에 저장된 정보는 tangent space에 있으며,

이것을 사용하기 위해서는 tangent space에서 world space로의 변환이 필요하다.

https://docs.unity3d.com/kr/2021.3/Manual/SL-SurfaceShaders.html

 

표면 셰이더 작성 - Unity 매뉴얼

빌트인 렌더 파이프라인에서 표면 셰이더는 조명과 상호작용하는 셰이더를 더욱 간단하게 작성하는 방법입니다.

docs.unity3d.com

 

'산대특 > 게임 UIUX 프로그래밍' 카테고리의 다른 글

Shader - triplaner  (1) 2024.02.26
Shader - Diffuse Warping  (0) 2024.02.22
Shader -Blinn Phong 스펙큘러  (0) 2024.02.22
Shader - Lambert  (0) 2024.02.20
Shader - Vertex  (0) 2024.02.19

차례대로 Standard, CustomLambert, HalfLambert

 

1. Standard

 

Shader "Custom/lambert"
{
    Properties
    {
        _MainTex("Main Texture", 2D) = "white"{}
        _NormalMap("NormalMap", 2D) = "bump" {}
        _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0
        sampler2D _MainTex;
        sampler2D _NormalMap;     
        float4 _Color;
        struct Input
        {
            float4 color : COLOR;
            float2 uv_MainTex;
            float2 uv_NormalMap;
        };

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            
            float4 c = tex2D(_MainTex, IN.uv_MainTex);
            float4 d = tex2D(_NormalMap, IN.uv_NormalMap);
            o.Normal = UnpackNormal (d);
            o.Albedo = c.rgb * _Color;
            o.Alpha = c.a;
        }
        // 빛을 만든다
       
        ENDCG
    }
    FallBack "Diffuse"
}

 

2. CustomLambert

Shader "Custom/customLambert"
{
    Properties
    {
        _MainTex("Main Texture", 2D) = "white"{}
        _NormalMap("NormalMap", 2D) = "bump" {}
        _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf _MyLambert
        #pragma target 3.0
        sampler2D _MainTex;
        sampler2D _NormalMap;     
        float4 _Color;
        struct Input
        {
            float4 color : COLOR;
            float2 uv_MainTex;
            float2 uv_NormalMap;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            float4 c = tex2D(_MainTex, IN.uv_MainTex);
            float4 d = tex2D(_NormalMap, IN.uv_NormalMap);
            o.Normal = UnpackNormal (d);
            o.Albedo = c.rgb * _Color;
            o.Alpha = c.a;
        }
        // 빛을 만든다
        float4 Lighting_MyLambert(SurfaceOutput s, float3 lightDir, float atten)
        {
            float4 final;
            float ndot1 = dot(s.Normal, lightDir);
            final.rgb = ndot1 * s.Albedo.rgb;
            final.a = s.Alpha;
            return final;
            }
        ENDCG
    }
    FallBack "Diffuse"
}

 

2-2. CustomLambert

Shader "Custom/customLambert"
{
    Properties
    {
        _NormalMap("Normal Map",2D) = "bump"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf _MyLambert noambient;
        #pragma target 3.0
        sampler2D _NormalMap;     
        struct Input
        {
            float4 color : COLOR;
            float2 uv_NormalMap;
        };
        void surf (Input IN, inout SurfaceOutput o)
        {
            float4 c = tex2D(_NormalMap, IN.uv_NormalMap);
            o.Normal = UnpackNormal (c);
            o.Albedo = 1;
            
        }

        float4 Lighting_MyLambert(SurfaceOutput s, float3 lightDir, float atten)
        {
            float4 final;
            //return float4(1,0,0,1);
            //노멀벡터와 조명벡터를 내적해라
            float ndot1 = dot(s.Normal, lightDir);
            final = saturate(ndot1);
            return final;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

3. HalfLambert

Shader "Custom/customLambert2"
{
    Properties
    {
        _MainTex("Main Texture", 2D) = "white"{}
        _NormalMap("NormalMap", 2D) = "bump" {}
        _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf _MyLambert
        #pragma target 3.0
        sampler2D _MainTex;
        sampler2D _NormalMap;     
        float4 _Color;
        struct Input
        {
            float4 color : COLOR;
            float2 uv_MainTex;
            float2 uv_NormalMap;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            float4 c = tex2D(_MainTex, IN.uv_MainTex);
            float4 d = tex2D(_NormalMap, IN.uv_NormalMap);
            o.Normal = UnpackNormal (d);
            o.Albedo = c.rgb * _Color;
            o.Alpha = c.a;
        }
        // 빛을 만든다
        float4 Lighting_MyLambert(SurfaceOutput s, float3 lightDir, float atten)
        {
            float4 final;
            float ndot1 = dot(s.Normal, lightDir)* 0.5 + 0.5;
            final = pow(ndot1, 3);
            final.rgb = ndot1 * s.Albedo.rgb * _LightColor0.rgb * atten;
            final.a = s.Alpha;
            return final;
            }
        ENDCG
    }
    FallBack "Diffuse"
}

 


 

CustomLambert의 기본형

 

1. #pragma surface surf _MyLambert

2. void surf (Input IN, inout SurfaceOutput o)

3. float4 Lighting_MyLambert(SurfaceOutput s, float3 lightDir, float atten)
 {
  ;
     return float4(1, 0, 0 , 1);

 }

 

우선 이 3가지로 스탠다드를 바꿔주고 메서드를 만들어준다.

이때 매개변수는 변경하거나 수정할 수 없으며 주어진 대로 사용해야 한다.

 

 

float3 lightDir
조명 방향의 벡터
단, 조명을 편하게 사용하기 위해 뒤집혀지고 길이가 1인 단위 벡터 상태

 

float atten(uation) - 감쇠
그림자를 받거나 거리가 멀어지면 점점 조명이 흐려지는 라이트의 거리별 현상

 

dot : 노멀 벡터와 라이트 벡터를 내적 연산해주는 함수

우리는 o.Normal에 값을 넣지 않았지만 s.Normal의 값을 가져올 수 있다.

 

lightDir

lightDir은 vertex에서 바라보는 조명의 방향을 뒤집은 조명 벡터이므로 두 벡터를 단순히 내적하면 cos값이 나온다.

 

나중에 ambient light 또는 추가라이트를 비출 때 이 음수는 문제를 일으킬수 있는데

이(0아래는 전부 0으로 잘라주는)를 해결해주는 함수 saturate, max가 있다.

saturate
max

이 함수들을 사용하면 빨간선처럼 0보다 작은수는 0으로 지정해준다.

 


 

HalfLambert

 

* 0.5 + 0.5는 마법의 숫자이다.

이 공식은 -1 ~ 1까지의 숫자를 0에서 1까지의 범위로 만들어준다.

그 결과 cos라인을 끌어 올려주어 부드러운 결과 값이 나온다.

 

조명의 색상 or 감쇠를 이용할 수 있다.

ex) final.rgb = ndotl * s.Albedo * _LightColor0.rgb * atten;

 

https://docs.unity3d.com/kr/2021.3/Manual/SL-UnityShaderVariables.html

 

빌트인 셰이더 변수 - Unity 매뉴얼

Unity의 빌트인 포함 파일에는 셰이더에 대한 전역 변수(예: 현재 오브젝트의 변환 매트릭스, 광원 파라미터, 현재 시간 등)가 포함되어 있습니다. 이러한 셰이더 프로그램에서 이러한 전역 변수

docs.unity3d.com

 

이에 따라 조명의 색깔을 변경하고 거리에 따른 그림자를 조절할 수 있다.

https://docs.unity3d.com/kr/530/Manual/StandardShaderMaterialParameterAlbedoColor.html

 

알베도 컬러 및 투명도 - Unity 매뉴얼

Albedo 파라미터는 표면의 베이스 컬러를 제어합니다.

docs.unity3d.com

 

Shader "Custom/fire"
{
    Properties
    {
        _MainTex ("Main Texture", 2D) = "white"{}
        _MainTex2 ("Main Texture2", 2D) = "white"{}
        _Speed("Speed", Range(0,10.0)) = 1
        _BrightNess("BrightNess", Range(0,1)) = 1
        _Color("Color",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"}

        CGPROGRAM
        #pragma surface surf Standard alpha:fade
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MainTex2;
        float _Speed;
        float _BrightNess;
        float _Color;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_MainTex2;
           
        };

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            //데이터 
            float2 d_uv = IN.uv_MainTex2;
            d_uv.y -= _Time.y;
           
            float4 d = tex2D(_MainTex2, d_uv * _Speed);

            //d.rgb
            //d.r => 0 

            //체크 
            float2 c_uv = IN.uv_MainTex;
            float4 c = tex2D(_MainTex, c_uv + d); 

            //체크를 보여주고 있음 
            o.Emission = c.rgb * _BrightNess;
            o.Alpha = c.a ;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

 


 

 

 

Shader "Custom/vertexcolor"
{
    Properties
    {
        _MainTex("Main Texture", 2D) = "white"{}
        _MainTex2("Main Texture2", 2D) = "white"{}
        _MainTex3("Main Texture3", 2D) = "white"{}
        _MainTex4("Main Texture4", 2D) = "white"{}
        
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
     

        CGPROGRAM
      
        #pragma surface surf Standard fullforwardshadows

        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MainTex2;
        sampler2D _MainTex3;
        sampler2D _MainTex4;


        
        struct Input
        {
            float4 color : COLOR;
            float2 uv_MainTex;
            float2 uv_MainTex2;
            float2 uv_MainTex3;
            float2 uv_MainTex4;


        };

       

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
           float4 c =tex2D(_MainTex, IN.uv_MainTex);
           //o.Emission = c.rgb + IN.color.rgb;

           IN.uv_MainTex2 -= _Time.y * 0.2;
           //float2 d_uv2 = IN.uv_MainTex2;
            //d_uv2.y -= _Time.y;
            float4 d = tex2D(_MainTex2,IN.uv_MainTex2);
           o.Emission = lerp(c.r, d.rgb, IN.color.r ); // 보이지 않는 곳을 0 보이는 곳을 1이라고 생각
           

           float4 e = tex2D(_MainTex3, IN.uv_MainTex3);
           o.Emission = lerp(o.Emission, e.rgb, IN.color.g);

           float4 f = tex2D(_MainTex4, IN.uv_MainTex4);
           o.Emission = lerp(o.Emission, f.rgb, IN.color.b);
           
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 


 

Shader "Custom/rock"
{
    Properties
    {
        _MainTex ("Main Texture", 2D) = "white"{}
        _MainTex2 ("Main Texture2", 2D) = "white"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MainTex2;

        struct Input
        {
            float4 color : COLOR; //버텍스 컬러 
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            //이끼 
            float4 d = tex2D(_MainTex2, IN.uv_MainTex);
            //돌맹이 
            float4 c = tex2D(_MainTex, IN.uv_MainTex);
            
            //이끼를 버텍스 컬러 R 부분에 출력하자 
            o.Albedo = lerp(c.rgb, d.rgb, IN.color.r);
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 


https://docs.unity3d.com/kr/530/Manual/StandardShaderMaterialParameterMetallic.html

https://docs.unity3d.com/kr/2022.1/Manual/StandardShaderMaterialParameterSmoothness.html

https://docs.unity3d.com/Manual/StandardShaderMaterialParameterNormalMap.html

 

Unity - Manual: Normal map (Bump mapping)

Normal map (Bump mapping) Normal maps are a type of Bump Map. They are a special kind of texture that allow you to add surface detail such as bumps, grooves, and scratches to a model which catch the light as if they are represented by real geometry. Unity

docs.unity3d.com

 

평활도 - Unity 매뉴얼

평활도(Smoothness) 개념은 스페큘러 워크플로우와 메탈릭 워크플로우 모두에 적용되며 매우 비슷한 방식으로 작동합니다. 디폴트로 메탈릭 또는 스페큘러 텍스처 맵이 할당되지 않았다면 슬라이

docs.unity3d.com

 

메탈릭모드: 메탈릭 파라미터 - Unity 매뉴얼

Metallic 워크플로에서 작업하는 경우(스페큘러 워크플로와 달리) 표면의 반사도 및 광원 반응이 메탈릭 레벨과 평활도 레벨에 따라 바뀝니다.

docs.unity3d.com

 

Shader "Custom/Metallic"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _NormalMap ("NormalMap", 2D) = "bump"{}
        _NormalStength("Normal Stength", Range(0.1, 2)) = 0
        _Occlusion("Occlusion", 2D) = "white"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _NormalMap;
        sampler2D _Occlusion;
        float _NormalStength;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_NormalMap;
        };

        half _Glossiness;
        half _Metallic;

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;

            float4 n = tex2D(_NormalMap, IN.uv_NormalMap);
            float3 normal = UnpackNormal(n);
            fixed4 occlusion = tex2D(_Occlusion, IN.uv_MainTex);

            o.Occlusion = occlusion;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;

            o.Normal = float3(normal.x * _NormalStength , normal.y * _NormalStength , normal.z);

            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

 

 

Shader "Custom/rockGolem2"
{
    Properties
    {
        _MainTex("MainTex",2D) = "white" {}
        _MainTex2("Left Hand",2D) = "white" {}
        _MainTex3("Right Hand",2D) = "white" {}
        _NormalMap ("NormalMap", 2D) = "bump"{}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _Occlusion ("Occlusion", 2D) = "white"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
       
        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MainTex2;
        sampler2D _MainTex3;
        sampler2D _NormalMap;
        half _Glossiness;
        half _Metallic;
        sampler2D _Occlusion;



        struct Input
        {
            float2 uv_MainTex;
            float2 uv_MainTex2;
            float2 uv_MainTex3;
            float4 color : COLOR;
            float2 uv_NormalMap;
        };
    
        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            //fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
            //o.Albedo = IN.color.rgb;
            //o.Alpha = c.a;
            IN.uv_MainTex2.y += _Time.y * 0.2;
            IN.uv_MainTex3.y += _Time.y * 0.2;
            fixed4 body = tex2D (_MainTex, IN.uv_MainTex);
            fixed4 lHand = tex2D (_MainTex2, IN.uv_MainTex2);
            fixed4 rHand = tex2D (_MainTex3, IN.uv_MainTex3);


            //float2 d_uv2 = IN.uv_MainTex2;
            //d_uv2.y -= _Time.y;




            //o.Albedo = lerp(0, body, IN.color.r);
            o.Albedo = lerp(body, lHand, IN.color.r);
            o.Albedo = lerp(o.Albedo, rHand, IN.color.b);

            float4 n = tex2D(_NormalMap, IN.uv_NormalMap);
            float3 normal = UnpackNormal(n);
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Normal = normal;
            o.Occlusion = tex2D(_Occlusion, IN.uv_MainTex);


        }
        ENDCG
    }
    FallBack "Diffuse"
}

하이어라키에 Sphere 구체 추가

ㅏ이어

폴더를 만든 후 Materials와 Shaders를 추가해준다

그 후

NewSurfaceShader를 New Material로 드래그해서 Assign

그 후

New Material을 Sphere에 드래그해서 Assign

코드 안에 내용들을 지워주는데 Input에 아무것도 없으면 오류가 난다.

이런식으로 프로티에 작성해주면 인스펙터에 생성이된다.

드래그한 코드

즉 o오브젝트에 Albedo를 float3(red, green, blue)를 할당하는데 red에만 1을 선언한 상태와 결과

 

Emission으로 바구면 평면으로 볼 수 있다.

 

Emission에 컬러속성을 할당 한후 값을 넣어주면

인스펙터에서 컬러창으로 색을 변경할 수 있다.

r g b에 각각 red blue green 변수를 선언후 할당한후

pow를 사용 pow(x,y) => x를 y 제곱

Red, Greed, Blue를 각각 이동하여 색을 변경할 수 있다.

BrightDarkness 변수 선언후 + 를 사용해서

Emission값을 flat(r,g,b)와 함께 사용

 

Texture 2D속성을 사용

float4 c = tex2D(_MainTex, IN.uv_MainTex);

그 후 만들어진 텍스쳐 필드에 텍스쳐 할당

https://docs.unity3d.com/Manual/SL-SurfaceShaders.html

 

Unity - Manual: Writing Surface Shaders

Surface Shaders and rendering paths Writing Surface Shaders In the Built-in Render PipelineA series of operations that take the contents of a Scene, and displays them on a screen. Unity lets you choose from pre-built render pipelines, or write your own. Mo

docs.unity3d.com

 

그림에서

name

min_damage

max_damage

item_type

4가지 속성을 가지고 배열을 만들어서 json 파일로저장

 

json파일을 만들때 " "가 중요하다.

 

만들고 나서 Format을 누르면 잘 작성이 됬는지 확인할 수 있다

코드를 작성 후 오류가 나지 않으면 이렇게 생성 된 걸 알 수 있다.

 

그 후 Text를 복사하고 메모장에 저장

저장할 때 주의

 

우선 Unity에 Resources 라는 폴더를 만들고 넣어주면 되는데 이 때 반드시 폴더명을 이렇게 해주어야 한다.

https://www.newtonsoft.com/json

 

Json.NET - Newtonsoft

× PM> Install-Package Newtonsoft.Json or Install via VS Package Management window. ZIP file containing Json.NET assemblies and source code: Json.NET

www.newtonsoft.com

Newtonsoft.Json은 C#에서 JSON 데이터를 처리하는 데 사용되는 강력한 라이브러. 주요 기능은 다음과 같다.

  1. JSON 직렬화(Serialization): C# 객체를 JSON 문자열로 변환합니다.
  2. JSON 역직렬화(Deserialization): JSON 문자열을 C# 객체로 변환합니다.
  3. LINQ를 사용한 JSON 쿼리(Query): LINQ 쿼리를 사용하여 JSON 데이터를 쉽게 쿼리하고 필터링할 수 있습니다.
  4. JSON 스키마(Validation): JSON 데이터가 주어진 스키마에 부합하는지 확인할 수 있습니다.
  5. JSON 파싱(Parsing): JSON 문자열을 해석하고 객체로 변환합니다.
  6. JSON 변환(Transform): JSON 데이터를 다른 형식으로 변환합니다.
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class HelmMain : MonoBehaviour
{
    
    void Start()
    {
        TextAsset asset = Resources.Load<TextAsset>("helm_data");
        string json = asset.text;
        Debug.Log(json);

        HelmData data = JsonConvert.DeserializeObject<HelmData>(json);
        //Debug.Log(data);  
        Debug.LogFormat("{0} {1} {2} {3}", data.name, data.min_damage, data.max_damage, data.item_type);

        //저장
        //직렬화 객체 생성
        HelmInfo info = new HelmInfo(data.min_damage, data.max_damage);
        //직렬화 시작(객체 ->문자열)
        string serializedJson = JsonConvert.SerializeObject(info);

        //파일로 저장
        //Application.persistentDataPath : 플랫폼 OS에 따라 경로를 자동으로 잡아줌
        string path = Application.persistentDataPath + "/helm_info.json";
        Debug.Log(path);
        //문자열을 파일로 저장
        File.WriteAllText(path, serializedJson); //경로, 문자열
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HelmData
{
    public string name;
    public int min_damage;
    public int max_damage;
    public string item_type;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HelmInfo
{
    public int min_damage;
    public int max_damage;

    public HelmInfo(int min_damage, int max_damage)
    {
        this.min_damage = min_damage;
        this.max_damage = max_damage;
    }
}

실행을 시켜보면

1. 데이터를 문자열을 출력

2. 데이터를 역직렬화 후 데이터를 잘 받고 있는지 확인

3. Helminfo클래스를 메인에서 불러온 후 생성자를 생성한뒤 지정한 경로 위치로 생성후 저장 => 그 위치 출력

 

보기와 같이 출력한 경로를 검색하면 지정된 경로에 파일이 저장됨을 확인 할 수 있다.

기존에 있는 에셋을 다운받아

가이드라인으로 잡고 명암을 줄이고

그 에셋을 찾아 UI를 본뜨는 작업을 하였는데

그것을 하기 전에 제일 기초이자 중요한 GameObject.onClick.AddListener()를 다루려 한다.

 

Button1을 클릭하면 콘솔에 찍힌다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
    [SerializeField] private Button btn;

    void Start()
    {
        btn.onClick.AddListener(() =>
        {
            Debug.Log("button clicked!");
        });
    }
};

 

Button1을 클릭하면 2, 3번이 사라진다.

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
    [SerializeField] private Button btn1;

    [SerializeField] private Button btn2;
    [SerializeField] private Button btn3;

    void Start()
    {
        btn1.onClick.AddListener(() => {
            this.btn2.gameObject.SetActive(false);
            this.btn3.gameObject.SetActive(false);
        });
    }
}

 

Button1을 클릭하면 2, 3이 사라지는데

Button2, Butto3을 배열(btns)로 만들어서 할당하였다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
    [SerializeField] private Button btn1;

    [SerializeField] private Button[] btns;

    void Start()
    {
        btn1.onClick.AddListener(() => {

            for (int i = 0; i < this.btns.Length; i++)
            {
                Button btn = btns[i];
                btn.gameObject.SetActive(false);
            }
        });
    }
}

 

Button1, Button2를 btns(배열)에 할당하였고

Button3을 누를시 배열이 사라지고 Button4를 누를시 배열이 다시 생기게 작성

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
    [SerializeField] private Button btn3;
    [SerializeField] private Button btn4;
    [SerializeField] private Button[] btns; //btn1, btn2

    void Start()
    {
        btn3.onClick.AddListener(() =>
        {
            Debug.Log("버튼3 눌렸습니다.");
            for (int i = 0; i < this.btns.Length; i++)
            {
                Button btn = btns[i];
                btn.gameObject.SetActive(false);
            }
        });

        btn4.onClick.AddListener(() =>
        {
            Debug.Log("버튼4 눌렸습니다.");
            for (int i = 0; i < this.btns.Length; i++)
            {
                Button btn = btns[i];
                btn.gameObject.SetActive(true);
            }
        });
    }
}

'산대특 > 게임 알고리즘' 카테고리의 다른 글

AppleCatch  (0) 2024.02.06
동기와 비동기  (1) 2024.02.05
직렬화와 역직렬화  (0) 2024.02.05
디자인 패턴과 싱글톤 패턴  (0) 2024.02.05
Process 와 Thread 그리고 Thread 와 Coroutine  (1) 2024.02.04

1. ground 요소를 추가하여  Rigidbody 2D -> Body Type - Kinematic 설정하여

땅을 만들어 공중에 떠있게 만들어 주었다.

2. 캔버스 레거시 text로 velocityText를 만들어서 고양이가 제자리부터 이동거리를 수치로 표시

    -  ClimbCloudGameDirector 스크립트를 추가하고 CatController에 연결해줌으로 써 이동거리 표시

3. 고양이 선택후 Tool bar에서 Window - Animation- Animation에 들어가서 Create를 누른후 

     Assets안에 애니메이션 폴더를 만들어주고 걷는 애니메이션을 넣을 것으므로 이름을 Walk로 생성

     Animation탭 안에서 add property를 누르고 sprite 선택 후 적적할게 고양이가 나눠져서 걷는 그림을 넣어주었다.

    마지막으로 애니메이션 탭 안 우측 상단에 있는 add keyframe을 누른 후 적당한 거리 뒤에 넣으면

    마지막 프레임이 복사되고 그 프레임 안까지의 사진들이 연속적으로 실행된다.

    애니메이터는 CatController와 같은 위치에 있으므로 따로 호출하지 않아도 클래스 안에서 사용가능

    애니메이션속도를 캐릭터 움직임 속도에 맞춰 변하도록 설정

    this.anim.speed = (Mathf.Abs(this.rbody.velocity.x) / 2f);

 

4. 물리엔진을 사용해서 충돌판정

1. 둘중 하나는 리지드바디 컴포넌트가 있어야 한다

2. 두 객체모두 콜라이더가 있어야 한다

3. isTrigger 모드를 체크 한다

 

 

트리거모드는 업데이트보다 먼저 일어난다

 

5. 깃발에 도달(충돌)했을 때 화면 전환

6. 그후 화면을 터치하면 다시 복귀

7. 고양이 이동가능 거리 조절

8. 고양이가 공중에서 계속 점프하지 못하게 조건문 추가

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Assertions.Comparers;
using UnityEngine.SceneManagement;

public class CatController : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rbody;
    [SerializeField] private float moveForce = 100f;
    [SerializeField] private float jumpForce = 680f;

    [SerializeField]
    private ClimbCloudGameDirector gameDirector;

    private Animator anim;

    private bool hasSpace = false;

    private void Start()
    {
        //this.gameObject.GetComponent<Animation>();

        anim = GetComponent<Animator>();

        //this.gameDirector = GameObject.Find("GameDirector").GetComponent<ClimbCloudGameDirector>();
        //this.gameDirector = GameObject.FindAnyObjectByType<ClimbCloudGameDirector>();

    }

    void Update()
    {
        //스페이스바를 누르면 
        if (Mathf.Abs(rbody.velocity.y) < 0.01f)

        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                if (!hasSpace)
                {
                    //힘을 가한다 
                    this.rbody.AddForce(this.transform.up * this.jumpForce);
                    //this.rbody.AddForce(Vector3.up * this.force);

                        hasSpace = false; // 다시 점프할 수 있도록 허용

                }
            }


        }
        // -1, 0, 1 : 방향 
        int dirX = 0;
        //왼쪽화살표키를 누르고 있는 동안에 
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            dirX = -1;
        }

        if (Input.GetKey(KeyCode.RightArrow))
        {
            dirX = 1;
        }

        // Debug.Log(dirX); //방향 -1, 0, 1

        //스케일 X를 변경 하는데 키가 눌렸을 때만 
        //키가 눌렸을때만 = (dirX != 0)
        if (dirX != 0)
        {
            this.transform.localScale = new Vector3(dirX, 1, 1);
        }


        //벡터의 곱 
        //Debug.Log(this.transform.right * dirX);  //벡터3

        //도전 ! : 속도를 제한하자 
        //velocity.x 가 3정도가 넘어가니깐 빨라지는거 같드라구...
        if (Mathf.Abs(this.rbody.velocity.x) < 3)
        {
            this.rbody.AddForce(this.transform.right * dirX * moveForce);
        }

        this.anim.speed = (Mathf.Abs(this.rbody.velocity.x) / 2f);
        this.gameDirector.UpdateVelocityText(this.rbody.velocity);


        // Debug.Log(this.transform.position);

        float clampX = Mathf.Clamp(this.transform.position.x, -2.39f, 2.35f);
        Vector3 pos = this.transform.position;
        pos.x = clampX;
        this.transform.position = pos;


    }

    // Trigger 모드 일경우 충돌 판정을 해주는 이벤트 함수
    private bool hasEntered = false;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (!hasEntered)
        {
            Debug.LogFormat("OnTriggerEnter2D: {0}", collision);
            SceneManager.LoadScene("ClimbCloudClear");
            hasEntered = true; // 한 번 이벤트가 발생하면 이 변수를 true로 설정하여 두 번 이상 호출되지 않도록 함

        }

    }



}

 

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

public class ChangeScene : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            
            SceneManager.LoadScene("ClimbCloud");
            Debug.Log("화면이 전환됨");
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ClimbCloudGameDirector : MonoBehaviour
{
    [SerializeField] private Text velocityText;

    public void UpdateVelocityText(Vector2 velocity)
    {
        float velocityX = Mathf.Abs(velocity.x);
        this.velocityText.text = velocityX.ToString();
    }
}


+ 개선할 점

 

카메라가 고양이 따라가기

'산대특 > 게임 알고리즘' 카테고리의 다른 글

Pirate Bomb - Captain(Enemy)  (0) 2024.02.02
Pirate Bomb - BombGuy  (0) 2024.02.02
ClimbCloud  (2) 2024.02.01
C# 대리자, 람다함수  (1) 2024.01.31
Git, SourceTree  (1) 2024.01.31

+ Recent posts