트리거에 자동차가 들어오면 레이저포인터로 가르키는 코드 해당 코드를 사용하는 게임 오브젝트는 LineRenderer 컴포넌트와 콜라이더 설정이 되어있어야 한다.

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

public class lineRendererTest : MonoBehaviour
{
    private LineRenderer lineRenderer;

    // Start is called before the first frame update
    void Start()
    {
        lineRenderer = GetComponent<LineRenderer>();
        // 라인이 가지개될 색상 표현
        Material material = new Material(Shader.Find("Standard"));
        material.color = new Color(0, 195, 255, 0.5f);
        lineRenderer.startWidth = 0.2f;
        lineRenderer.endWidth = 0.2f;
        // 끝점과 시작점 2개
        lineRenderer.positionCount = 2;
    }

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

    private void OnTriggerStay(Collider other)
    {
        if (other.tag == "Car")
        {
            lineRenderer.SetPosition(0, transform.position);
            lineRenderer.SetPosition(1, other.transform.position);
        }

    }

    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "Car")
        {
            lineRenderer.SetPosition(1, transform.position);
        }
    }
}

 

레이저 메터리얼을 위 그림과 같은 설정으로 변경하여준다. 그렇지 않으면 레이저에 색상이 검정색이나 분홍색만 나오고 설정한 색상을 표현하지 않는다.

Invoke란 호출이라는 의미를 가지고 있다.

 

Unity에는 Invoke이라는 함수가 존재한다. 

이 함수를 사용하는 방법은

Invoke("지연 시킬 함수 명", 지연시간f);

이 함수는 특정 함수를 특정 시간 뒤에 수행할 수 있도록 하는 함수이다.

 

다음으로 InvokeRepeating이라는 함수도 존재한다. 

이 함수를 사용하는 방법은 

InvokeRepeating("지연 및 반복시킬 함수 명", 처음 지연시간f, 지연후 반복할 시간f);

 

 

다음으로 유용하게 사용할 수 있는 함수로 contains함수가 존재한다.

https://ponyozzang.tistory.com/331

 

C# Contains 값이 존재하는지 확인하는 방법 및 예제

C#에서 문자열이나 배열, 리스트에 지정한 문자가 포함되어 있는지 또는 이미 등록되어 있는지 확인하는 방법으로 Contains 또는 정규 표현식을 사용합니다. 이번에는 Contains를 사용하여 문자열이

ponyozzang.tistory.com

사용 형식은

문자열.contains("str");
리스트.contains("str");
배열.contains("str");

원하는 단어를 문자열이 포함하였는지 확인하는데 도움을 주는 함수이다.

이번에는 싱글턴 패턴을 활용하여 안드로이드의 많은 기능을 활용하는 코드를 간단하게 보여드리겠습니다!

참고로 밑의 유튜브는 제가 참고한 영상입니다.

 

이번 시간에는 권한 설정 + 권한이 필요한 기능 사용입니다.

 

https://www.youtube.com/watch?v=B7a6xpMXDYE&t=502s

저는 이번에 유니티 툴에서 안드로이드 스튜디오의 메서드 getAllCellInfo()를 사용하고 싶었습니다.

그를 위한 코드를 보여드리겠습니다.

 

package com.example.nrlibrary;

import android.annotation.SuppressLint;
import android.content.Context;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.TelephonyManager;
import android.widget.Toast;

import java.util.List;

public class cellnr {

    private static TelephonyManager telephonyManager;
    private static cellnr m_instance;
    private static Context context;

    // 싱글턴 메소드
    public static cellnr instance(Context ct) {
        if (m_instance == null) {
            context = ct;
            telephonyManager = (TelephonyManager) ct.getSystemService(ct.TELEPHONY_SERVICE);
            m_instance = new cellnr();
        }
        return m_instance;
    }

    private void ShowToast(String msg, int i) {
        // i는 짧게 아니면 길게 화면에 보여줄 것인지 체크한다.
        if (i == 0) // 짧은 경우
        {
            Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
        } else { // 길게 보이는 경우
            Toast.makeText(context, msg, Toast.LENGTH_LONG).show();

        }
    }
    
    @SuppressLint("MissingPermission")
    String easy_cell_info() {
        TelephonyManager tm = (TelephonyManager) context.getSystemService(context.TELEPHONY_SERVICE);
        return tm.getAllCellInfo().toString();
    }

위 코드를 설명하겠습니다.

우선 TelephonyManager와 Class 그리고 Context를  static을 활용하여 선언하였고

instance 싱글턴 메서드를 통해서만 접근할 수 있게 하였습니다.

 

telephonyManager 같은 경우 위치 정보의 권한이 필요로 합니다. 

따라서 easy_cell_info 메소드를 보면 위에 MissingPermission이라고 되어 있습니다.

이것은 간단하게 권한 사용을 거부당했는지 체크하는 함수를 사용하지 않겠다는 의미입니다.

기본적으로는 권한 사용 거부 여부를 체크하는 조건문을 달아야 합니다.

하지만 여기서 체크하게 되면 뭔가 오류가 생겨서 그냥 무시하고 유니티에서 체크하도록 하였습니다.

 

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

public class check_5G : MonoBehaviour
{
    private AndroidJavaObject UnityInstance;
    private AndroidJavaObject UnityActivity;
    public Text text;

    void Start()
    {
        AndroidJavaClass ajc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        UnityActivity = ajc.GetStatic<AndroidJavaObject>("currentActivity");

        AndroidJavaClass ajc2 = new AndroidJavaClass("com.example.nrlibrary.cellnr");
        UnityInstance = ajc2.CallStatic<AndroidJavaObject>("instance", UnityActivity);


        StartCoroutine(DelayEffect());
    }
    private IEnumerator DelayEffect()

    {

        while (true)

        {
            ShowCell5();

            /** 1초 딜레이 **/

            yield return new WaitForSeconds(1f);
        }

    }

    public void ShowCell5()
    {
        text.text = UnityInstance.Call<string>("easy_cell_info");

    }

 위 코드는 유니티 코드입니다.  start에서 하고 있는 작업은 간단하게 싱글턴 패턴 메서드 instance를 사용하기 위한

작업과 코 루틴 함수 getCellInfo() 통신 정보 획득 함수를 사용하고 있습니다.

ShowCell5에 있는 easay_cell_info를 사용하기 위해서는 미리 위치 정보를 확인할 수 있는 권한이 있어야 합니다.

 

유니티에서 권한 설정을 위해 AndroidManifest.xml 파일이 필요합니다. 파일의 위치는

저 같은 경우 아래와 같았습니다.

C:\Program Files\Unity\Hub\Editor\2019.2.17f1\Editor\Data\PlaybackEngines\AndroidPlayer\Apk

 

일반적인 경우로는

- Unity 설치 폴더/Editor/Data/PlaybackEngines/AndroidPlayer/Apk/AndroidManifest.xml

- 대상 경로 : {Project Root}/Assets/Plugins/Android/AndroidManifest.xml

에서  AndroidManifest.xml 파일이 있고  AndroidManifest.xml 파일을 가져와서

Plugins/Android폴더 안에 넣어서 사용할 수 있습니다.

저 같은 경우 위치 정보와 휴대폰 상태 정보가 필요하여 아래와 같이 퍼미션을 사용한다고 xml 파일에 명시하였습니다.

 

 

 

저는 위치 정보 권한을 시작하자마자 획득하기 위해서 아래의 코드를 사용하였습니다.

아래의 코드는 어느 블로거 님의 글을 통해 학습한 내용을 제가 필요한 코드로 바꿔 사용한 코드이며

설명은 블로거 님의 블로그를 통해 자세히 알아보실 수 있습니다.

https://mentum.tistory.com/150

 

유니티 퍼미션 체크 적용기. (Unity Permission Check) 2019.11.14 재작성.

2019.11.14 재작성. 유니티 안드로이드에서 스크린샷을 저장하기 위해 권한 부분을 다시 작성해야해서 하는 김에 이 글도 재작성 하였다. 2019/11/14 - [Unity/프로그래밍] - 유니티 안드로이드 스크린샷

mentum.tistory.com

 

using System;
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Android;

// 매니페스트 선언필요.  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
public class PermissionCheck : MonoBehaviour
{
    [SerializeField] private GameObject panel; 
    bool onCheck = false;

    private void Start()
    {
        if (onCheck == false)
        {
            StartCoroutine("PermissionCheckCoroutine");
            panel.SetActive(true);
        }
    }

    public void PressBtnCapture()
    {
        if (onCheck == false)
        {
            StartCoroutine("PermissionCheckCoroutine");
            panel.SetActive(true);
        }
    }

    IEnumerator PermissionCheckCoroutine()
    {
        onCheck = true;

        yield return new WaitForEndOfFrame();
        if (Permission.HasUserAuthorizedPermission(Permission.FineLocation) == false)
        {
            Permission.RequestUserPermission(Permission.FineLocation);

            yield return new WaitForSeconds(0.2f); // 0.2초의 딜레이 후 focus를 체크하자.
            yield return new WaitUntil(() => Application.isFocused == true);

            if (Permission.HasUserAuthorizedPermission(Permission.FineLocation) == false)
            {
                // 다이얼로그를 위해 별도의 플러그인을 사용했기 때문에 주석처리. 그냥 별도의 UI를 만들어주면 됨.
                //AGAlertDialog.ShowMessageDialog("권한 필요", "스크린샷을 저장하기 위해 저장소 권한이 필요합니다.",
                //"Ok", () => OpenAppSetting(),
                //"No!", () => AGUIMisc.ShowToast("저장소 요청 거절됨"));

                OpenAppSetting(); // 원래는 다이얼로그 선택에서 Yes를 누르면 호출됨.
                onCheck = false;
                yield break;

            }
        }

        // 권한을 사용해서 처리하는 부분. 스크린샷이나, 파일 저장 등등.

        onCheck = false;
    }





    // 해당 앱의 설정창을 호출한다.
    // https://forum.unity.com/threads/redirect-to-app-settings.461140/
    private void OpenAppSetting()
    {
        try
        {
#if UNITY_ANDROID
            using (var unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            using (AndroidJavaObject currentActivityObject = unityClass.GetStatic<AndroidJavaObject>("currentActivity"))
            {
                string packageName = currentActivityObject.Call<string>("getPackageName");

                using (var uriClass = new AndroidJavaClass("android.net.Uri"))
                using (AndroidJavaObject uriObject = uriClass.CallStatic<AndroidJavaObject>("fromParts", "package", packageName, null))
                using (var intentObject = new AndroidJavaObject("android.content.Intent", "android.settings.APPLICATION_DETAILS_SETTINGS", uriObject))
                {
                    intentObject.Call<AndroidJavaObject>("addCategory", "android.intent.category.DEFAULT");
                    intentObject.Call<AndroidJavaObject>("setFlags", 0x10000000);
                    currentActivityObject.Call("startActivity", intentObject);
                }
            }
#endif
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }
    }
}

안녕하세요 요즘 장마철이 끝나지 않고 비가 계속 쏟아지는데 답답하네요 ㅎㅎ 새로운 동내로 이사 와서 첫 주말 자전거 타고 둘러보려 했는데 아쉽네요 그런 관계로 안드로이드 플러그인 사용에 대해 알려드리겠습니다!

 

저는 처음 사용할 때는 삽질을 많이 했지만 여러분은 간단히 되길 바라면서 시작해보겠습니다.

 

 

우선 안드로이드 프로젝트를 만들어 주시기 바랍니다. 

empty Activity 로 하셔도 되고 

No Activity를 하셔도 무방합니다.

저는 일단 처음에는 안드로이드에서 만든 메서드를 사용해보고 잘 되는지 확인하기 위해서 Empty Activity로 시작해서 실제 구동되는지 확인하고 넘어갔었습니다.

 

 

 

다음 화면입니다.

여기서 주의하셔야 할 것은 Minimum SDK 입니다. 

그 이유는 유니티에서도 Minimum SDK를 설정하는데 안드로이드 Minimum과 유니티 Minimum이 일치하지 않으면 오류가 생기게 됩니다. 잘 기억해두셨다가 유니티에서도 같게끔 설정해주세요

 

유니티 프로젝트가 성공적으로 생성되고 gradle 파일들도 성공적으로 빌드되었다면 File -> New -> New Module로 들어가 모듈을 생성해줍니다. 타입은 Android Library로 선택해주세요

 

 

모듈이 생성되면 androidTest, test 파일을 지워주세요 저는 진행하다 까먹어서 후에 지웠습니다. 이 파일들이 같이 있으면 중복으로 오류가 생길 수도 있답니다.

 

 

생성된 모듈에 클래스를 생성해주세요. 저는 test_String으로 만들겠습니다.

 

아래의 이미지와 같이 문자열 반환 메소드를 만들었습니다. 우선 플러그인이 되는지 확인을 위해 간단한 문자열 반환 함수만 사용하겠습니다. 기본적으로 함수를 static을 붙여서 만든 이유는 static을 안 붙이고 유니티에서 호출하면 호출이 되질 않습니다. static을 안 붙이고 사용하려고 하신다면 싱글턴 패턴을 이용하셔야 합니다. 다음 글에서 다루겠습니다.

이제 Build -> Make Module '모듈명' 을 클릭해주세요

 

그다음으로, 생성한 모듈을 우클릭하여 Show in Explorer을 선택해서 폴더를 엽니다.

 

 

mylibrary를 클릭(본인의 모듈명)

 

outputs를 클릭

 

 

aar을 클릭

 

aar 파일이 만들어 진 것을 볼 수 있습니다. 

이 파일을 이용해서 유니티에서 안드로이드에서 구현된 메서드를 사용할 수 있도록 할 수 있습니다.

 

이제 유니티로 넘어가겠습니다.

 

유니티 프로젝트를 생성해주세요

생성되었다면, 위 탭 창에서 File -> Build Setting 을 클릭해주세요

플랫폼을 Android로 설정해주세요.

다음으로, Player Setting... 을 클릭해서 Other Setting에있는 Identification에서 Minimum API Level을 안드로이드 Minimum SDK 레벨과 동일하게 설정해주세요 중요합니다.

 

 

다음으로 위로 올리면 Company Name이 있습니다. DefaultCompany로 남겨두지 마시고 다른 아무 이름이나 원하는 이름으로 변경해주세요

 

 

다음으로 폴더를 생성하겠습니다. 

Assets -> Plugins -> Android 순으로 폴더를 제작해주세요.

 

그곳에 아까 만들어둔 aar 파일을 넣어주세요

이제 Assets 폴더에다 C# 스크립트를 제작하도록 하겠습니다. 코드는 일단 아래 이미지와 같이 만들어 주세요

 

코드를 보면 new AndroidJavaClass(""); 부분에 보면 빈 문자열을 넣고 있습니다.

안드로이드 프로젝트로 돌아가서 패키지 경로를 확인해주세요

 

 

저 같은 경우 com.example.mylibrary라고 돼있는 것을 확인할 수 있습니다.

 

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

public class NewBehaviourScript : MonoBehaviour
{
    private AndroidJavaObject UnityInstance;
    [SerializeField] private Text text;

    // Start is called before the first frame update
    void Start()
    {
        AndroidJavaClass ajc = new AndroidJavaClass("com.example.mylibrary.test_String");
        string str = ajc.CallStatic<string>("returnString");

       text.text = str;
    }

}

ajc.CallStatic은 스태틱 메서드를 호출할 때 사용합니다.

ajc.Call 은 일반 메소드를 호출할 때 사용합니다.

Call로 호출을 원한다면 static으로 선언된 싱글턴 메서드를 호출한 후 생성된 Instance를 통해 가능합니다.

아래와 같이 객체를 생성하여 주시기 바랍니다.

PluginManager는 엠티 오브젝트입니다.

 

 

플로그인 매니저에 방금 만든 스크립트를 넣어주고 Text에 Text를 드래그 앤 드롭해주세요 무슨 말인지 알죠?

 

UI text 사이즈를 위와 같이 키우고 폰트 사이즈를 50 정도로 맞춘 다음 글자를 가운데 맞춤을 해주세요

성공적이네요 ㅎㅎ 위 이미지는 모바일에서 구동된 이미지입니다. 그냥 유니티 환경에서 플레이 하면 문자열이 보이지 않게 되네요

일단 간단하게 유니티 플러그인을 실수 없이 만들 수 있도록 구현해보았습니다.

하지만 유니티에서 안드로이드를 사용하고 싶다는 것은 모바일 기능을 이용하고 싶다는 것일 것입니다.

그러면 유니티로 안드로이드 Context를 불러와서 사용해야 하거나 권한 설정을 해야 할 수 있습니다. 그 점에 대해서는 다음 글에서 다루겠습니다. 수고하셨습니다~

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

public class move_arrow : MonoBehaviour
{

    public int arrow_pos { get; set; } = 1;
    private IEnumerator arrow_trans()
    {
        while (true)
        {
            switch (arrow_pos)
            {
                case 1:
                    transform.localPosition = new Vector3(-500, 200, -277);
                    break;
                case 2:
                    transform.localPosition = new Vector3(-290, 200, -277);
                    break;
                case 3:
                    transform.localPosition = new Vector3(-100, 200, -277);
                    break;
                case 4:
                    transform.localPosition = new Vector3(90, 200, -277);
                    break;
                case 5:
                    transform.localPosition = new Vector3(280, 200, -277);
                    break;
                case 6:
                    transform.localPosition = new Vector3(480, 200, -277);
                    break;
            }

            /** 1초 딜레이 **/
            yield return new WaitForSeconds(3f);
        }

    }

    private void OnEnable()
    {
        StartCoroutine(arrow_trans());
    }

}

<코드 설명>

transform.position 으로 하지 않고 transform.localPosition으로 한 이유는 객체가 어떤 부모 객체에 속하기 때문이다.

 

<실행 결과>

 

 

리지드 바디가 없을 때
리지드 바디가 적용된 경우

 

트리거 인식을 위해 객체는 리지드 바디 컴포넌트를 가지고 있어야 한다. 그렇지 않으면 트리거에 들어와도 인식되지 않는다.

 

 

유니티에서 UI버튼을 마우스를 이용해 클릭하기 위해서는 Standalone Input Module이 컴포넌트로 구현 되어 있어야 가능하다.

 

유니티 인스펙터 창에서 게임오브젝트 값을 넣을 수 있게 하기위해서는 2가지 방법이 있다.

하나는 접근 지정자를 public으로 하면 된다.

나머지 한가지 방법으로는 [SerializeField] 을 활용하면 priivate 접근지정자를 사용하면 된다. 

 

 

gotoPlayerAndFertilize() 함수 사용 모습

 

NPC에 적용한 스크립트와 Nav Mesh Agent

 

Nav Mesh Agent는 콜라이더 처럼 테두리가 있다.

테두리가 객체보다 크면 객체가 장애물을 테두리가 클 수록 크게 돌아간다.

사이즈를 적당하게 조절하지 않으면 객체가 공중에 떠서 이동하는 경우도 있었다.

 

Navigation_farmer

 

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

public class Navigation_farmer : MonoBehaviour
{
    Rigidbody myRigid;
    [SerializeField] private float moveSpeed;
    private static Vector3 originPos;
    private static NavMeshAgent agent;
    [SerializeField] private Transform tf_Destination;
    private bool justone;
    private static bool goto_on;

    DateTime StartTime;
    DateTime EndTime;

    // Start is called before the first frame update
    void Start()
    {
        myRigid = GetComponent<Rigidbody>();
        agent = GetComponent<NavMeshAgent>();
        originPos = transform.position;
        StartCoroutine(checkLotation());
        justone = true;

        gotoPlayerAndFertilize();
    }


    private IEnumerator checkLotation()
    {
        while(true)
        {
            if (Vector3.Distance(transform.position, originPos) < 0.5f)
            {
                if(LookAtPlayer.player_in_farmerTrigger == false)
                    transform.rotation = Quaternion.Euler(0, 90, 0);
                if (goto_on == false)
                    farmer_animator.IsMove = false;
                LookAtPlayer.ShowUI = true;
            }
            else
                LookAtPlayer.ShowUI = false;

            yield return new WaitForSeconds(1f);
        }
    }

    public  void gotoPlayerAndFertilize()
    {
        goto_on = true;
        farmer_animator.IsMove = true;
        agent.SetDestination(tf_Destination.position);
        
        StartCoroutine(waitMeeting());



    }

    static public void gotoOrigin()
    {
        goto_on = false;
        agent.SetDestination(originPos);
        farmer_animator.IsMove = true;
    }

    private IEnumerator waitMeeting()
    {
        while(true)
        {
            if(Vector3.Distance(transform.position, tf_Destination.position) < 0.3f)
            {
                farmer_animator.IsMove = false;
                farmer_animator.Fertilize_on = true;
                break;
            }
            yield return new WaitForSeconds(1f);
            
        }
    }
}

 

 

네비게이션을 사용하기 위해선 map 을 Navigation Static 설정해주어야 한다.

 

다음 bake를 통해 지형을 ai가 인식할 수 있도록 해야한다.

 

<bake를 하기위한 경로>

 

탭에서 Window를 선택 -> AI 를 선택 -> Navigation 선택 

눌렀으면 Inspector 창 옆에 Navigation이 생성된다.

 

 

 

 

<추가 정보>

 [SerializeField] 를 변수 앞에 붙이면 Unity Inspector 창에서 값을 볼 수 있게되며 설정 또한 할 수 있게된다.

 

Vector3.Distance(transform.position, originPos) < 0.5f

위 함수는 2가지 객체의 position 정보를 받아서 위치를 비교하여 값을 반환하는 함수이다.

 

 

agent = GetComponent<NavMeshAgent>();

agent.SetDestination(originPos);

 

위 agent 객체를 통해 목표점으로 이동할 수 있다.

 

 

비료를 준 후 원래 자리로 이동을 위해 아래 함수에 추가된 gotoOrigin()

    public void invisible_Fertilize_box()
    {
        if (Fertilize_on)
        {
            Fertilize_on = false;
            Navigation_farmer.gotoOrigin();
        }
    }

 

OnEnable 정의

인스펙터뷰에서 체크를 하게되면 게임 오브젝트를 활성화 할 때 실행된다. 활성화 될 때마다 호출 됩니다.

따라서 위의 메소드는 객체를 비활성화 하거나 활성화로 상태를 자주 바꿀 때 유용하게 사용할 수 있습니다.

아래의 사이트를 통해 더 자세하게 공부 할 수 있습니다.

https://itmining.tistory.com/47

 

[유니티 기초] 유니티 생명주기 (Life Cycle)

이 글은 PC 버전 TISTORY에 최적화 되어있습니다. 유니티의 생명주기 유니티는 사용자가 호출하지 않아도 호출되는 함수들이 있습니다. 그 함수의 호출 주기를 유니티의 생명주기(LifeCycle)이라고 ��

itmining.tistory.com

 

코루틴 함수

요즘 유니티 코드를 작성하다 보면 게임을 조금씩 완성해 나가다 보면 Update에 코드를 많이 사용하게 되는데, 그럴 수록 프로세스에 부담이 갈 것을 느꼈다. 그러던 중 코루틴 함수를 만났다. 대부분의 코드가 메 프레임마다 상태를 업그레이드 할 필요는 없다. 따라서 코루틴 함수를 활용하는 것은 유니티로 게임 개발하는 사람들에게는 매우 중요하다 느낀다.

기본적으로 아래의 코드 처럼 작성해서 활용하면 된다.

 

private IEnumerator DelayEffect()

    {

        while (true)

        {

            method();

            /** 1초 딜레이 **/

            yield return new WaitForSeconds(1f);

        }

    }

 

코루틴 함수 사용 방법

StartCoroutine(Method());

 

자주 했던 실수

OnEnable()에 코루틴 함수를 적용 시키고 Start에 초기화를 진행하면 OnEnable()이 더 빨리 실행되기 때문에 코드가

재대로 동작 하지 않을 수 있다.

lookAt을 사용해 사용자를 npc가 바라보게 하였다.

그런데 한가지 문제가 발생했다.

npc가 플레이어를 잘 바라본다, 하지만 위를 바라보고 있었다.

하지만 정면을 바라봐야 제대로 되었다 할 수 있기 때문에 정면을 보게끔 코드를 수정하였다.

플레이어의 x,z축만 가져오고 y축은 사용하지 않는 것이다. 

 

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class LookAtPlayer : MonoBehaviour

{

    public GameObject target;

    private Vector3 targetPosition;

 

    private void OnTriggerStay(Collider other)

    {

        // other.gameObject == target.gameObject

        if (other.tag == "Player")

        {

            targetPosition = new Vector3(other.transform.position.x, transform.position.y, other.transform.position.z);

            transform.LookAt(targetPosition);

        }

    }

}

 

 

코로나도 이제 조금씩은 진정되고 있는 오늘

 

유니티로 감자를 자라게 해보겠습니다.

 

우선 영상을 보겠습니다.

https://youtu.be/G_78W35p164

 

<참고>

<window 10 동영상 찍은 방법>

win + G을 누르면 윈도 화면을 찍을 수 있는 툴이 나옵니다. 그것을 이용해

영상을 촬영했습니다. 그리고 밑의 편집기를 이용해 용량을 줄였습니다. 용량이 21메가에서 0.22로 줄었습니다.

용량이 98 퍼나 줄어드네요 

 

https://www.videosmaller.com/ko/

 

온라인으로 동영상 파일 크기 줄이기, 온라인으로 동영상 압축하기 (MP4, AVI, MOV, MPEG) | VideoSmaller

온라인으로 동영상 파일 크기를 줄이고, 동영상을 압축해 보세요. 화질 저하 없이 MP4 동영상 크기를 줄이고, 동영상을 더 작게 만들어 보세요.

www.videosmaller.com

 

우선,첫 번째로 유니티의 hierarchy 창을 보겠습니다.

 

 

 

potatos란 감자들의 집합이고

potato_번호 가 감자 객체입니다.

그 안에 있는 potato_25, potato_45, potato_90 은 감자의 형태입니다.

뒤의 숫자는 감자를 심고 지난날입니다. 

 

감자 집합(potatos)에는 Check_Today라는 스크립트를 사용합니다. 

위의 스크립트(Check_Today) 특징으로 Today라는 변수를 static을 사용하여 프로그램 초기부터 객체가 생성되게 하였고

public을 사용하여 외부 모듈들도 위의 변수를 참조할 수 있게 하였습니다.

Today 변수는 모든 감자들의 성장에 영향을 미칩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class check_Today : MonoBehaviour
{
    public static int Today;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Today: " + Today);
    }
 
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Today += 5;
            KeyDown_Space();
        }
    }
    private void KeyDown_Space()
    {
        Debug.Log("Today: " + Today);
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

 

다음으로 감자 객체(potato_01, ... , potato_06)에 Grow_up_potato라는 스크립트를 사용했습니다.

위 스크립트는 Check_Today.Today의 변수 값에 따라서 식물을 자라게 만드는 스크립트입니다.

하나의 감자 객체는 25일 된 날 감자, 45일 된날 감자, 90일 된날 감자가 들어있습니다.

초기에는 45, 90일 된 감자들은 비활성화하여 보이지 않게 하였습니다.

그리고 today값이 45가 되기 전까지는 25일 된 날 감자 크기를 키워주며

45일이 되면  45일 된 감자 형태를 활성화하고 25일 된 감자를 비활성화합니다.

이후 90일이 되기 전까지 이번에는 y축을 조금씩 성장시킵니다. 

그리고 90일이 되면 감자가 최종 성장 형태가 됩니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Grow_up_potato : MonoBehaviour
{
    int today;
    GameObject potato_25;
    GameObject potato_45;
    GameObject potato_90;
 
    // Start is called before the first frame update
    void Start()
    {
        today = 0;
        potato_25 = transform.GetChild(0).gameObject;
        potato_45 = transform.GetChild(1).gameObject;
        potato_90 = transform.GetChild(2).gameObject;
    }
 
    // Update is called once per frame
    void Update()
    {
        today = check_Today.Today;
        growing_up(today);
     
    }
 
    void growing_up(int today)
    {
        //if (today < 20)
        //{
        //    if (potato_25.activeSelf == true)
        //        potato_25.SetActive(false);
        //}
        if (today >= 0 && today < 45)
        {
            if (potato_25.activeSelf == false)
                potato_25.SetActive(true);
            float size = (float)today * 0.1f;
            float size_y = (float)today * 0.2f;
            potato_25.transform.localScale = new Vector3(size, size_y, size);
 
        }
        else if (today >= 45 && today < 90)
        {
            if (potato_25.activeSelf == true)
                potato_25.SetActive(false);
            if (potato_45.activeSelf == false)
                potato_45.SetActive(true);
 
            float size_y = ((float)today * 0.01f) + 0.3f;
            potato_45.transform.localScale = new Vector3(1, size_y, 1);
 
        }
        else if (today >= 90)
        {
            // 여기서 potato_25의 상태를 비활성화 하지 않는 이유는
            // 45~90 동안 이미 비활성화가 되었기 때문이다.
            if (potato_45.activeSelf == true)
                potato_45.SetActive(false);
            if (potato_90.activeSelf == false)
                potato_90.SetActive(true);
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

감자 식물은 

현재 졸업 작품 준비중므로 공유할 수 없습니다.

+ Recent posts