챗GPT는 좋은 문명임

개발/내꿈은 팔이피플

2024. 6. 23. 05:33

https://youtu.be/9iI0-OdRzHI?si=O-EJ4Bflvvo2anUr

 

↑ 브금

 

 

 

오늘 포스팅의 목적: 게임 제작하면서 유용했던 에셋 추천, 그리고 챗지피티에 대한 사담

 

 

티스토리는 당장 이런 개웃긴 이모티콘을 추가하라

 

사실 19일까지가 최종 제출일(학교.)이어서.. 급하게 만들어서 제출은 해놨다

원래 출시까지가 목표긴 했는데 알파테스트까지 돌리고 버그제보가 너무 많아서(ㅋㅋ;;;)

좀 수정한 뒤에 7월 중순즈음에 출시하지 않을까 생각 중..

이걸로 부자가 되고 싶습니다..

 

 

UI디자인 같은 부분은 처음 의도했던 대로 IOS를 참고했다

제발 팀쿡이 나를 고소하지 않길. 🙏

 

작업을 열심히 해준 그래픽 담당에게 매우 감사하고 있다..

나는 스크립트 작업에선 40%정도를 맡은 것 같고

전체적인 코딩이나 Unity 작업은 혼자 해냈다

하면서 많은 것을 배웠는데.... 막상 블로그에 쓰려고 하니까 뭐 생각이 안나네

 

모바일도 그렇고 데스크탑도 그렇고 기기별 해상도 대응이 참 열받는 것 같다

내 핸드폰(노트10)부터 길쭉한 핸드폰(z플립 시리즈...) 까지는 다 OK되도록 해놨는데

알파테스트 돌려보면서 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

누가 갤탭으로 이걸 실행시켜서ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

탭 해상도에서는 UI가 이상하게 배치되는 부분이 있다는걸 깨달아가지고...

그 부분도 수정하면서 좀 눈물을 흘렸다

 

그래서 나름 이스터에그도 넣음

 

여기서부터는 좀 안 궁금할수도 있는 사담이라 접은글 처리함

 

 

더보기

사실 내가 좀 더 고수였다면 이런 문제가 발생하지 않았을 수도 있었을 것 같은데

학교에서 유니티는 안 가르쳐줬는데 어쩌라고요?

유니티 수업이 1학년인가 2학년인가 딱 1학기밖에 없었던걸로 기억함

 

이런 口大~ㅋㅋ

 

유니티 작업하는 거 보면서 혜민이가

너 이걸 어떻게 다 공부했냐..........? 안 배운 거 아냐?

라고 물어봤는데.

진심 듣자마자 그러게...? 라는 생각만 들고 아무런 생각도 할 수 없었음

 

 

아무튼 물건 구매 시스템도 완성은 시켰고

 

뱅크어플에 입출금내역도 잘 출력되고

 

인스타 디엠 답장도 잘 되고

 

클리커도 작동된다

 

구글검색 하다보면 씬 이동 최소화하라는 말이 있어서?

이번에는 그냥 씬 하나에 모든.... 것을 몰아넣었다

근데 확실히 씬 하나에 모든 것을 넣어놓으니까 편한 부분이 많은 것 같다

왜 편하냐면.. 그냥 편했어요.

저도 추천합니다.

 

사실 유니티는 거의 야매로 공부한거나 다름이 없어서

내가 맞게 하는건지 더 편한 방식이 있는지? 조차 잘 모르는 상태긴 하다..

일단 게임이 잘 굴러가는것에 만족하고 있기는 한데....

좀 주먹구구식으로 하는 부분이 많은 것 같다

예를 들면 이런 거

 

항상 이런 기분이 됨

 

 

 

아무튼 오늘 글의 본 목적 1

~유용했던 에셋 편~

 

 

구매하고나서 유용하게 쓰고 있는 에셋이 몇 가지 있는데

 

 

1. Eazy Sound Manager

이거 너무 편하다..

 

사실 다른 게임을 만들 때는 사운드매니저 그냥 손수 만들어서 썼는데..

아니 이런 무료에셋이 있다니

당연히 쓰는게 이득 아닙니까?

 

기능은 매우 간단하다

전체볼륨조절/브금볼륨조절/SFX볼륨조절 을 슬라이더에 연동할 수 있고,

코드로 사운드 출력 가능하고,

... 끝임

 

근데 공식문서? 설명이 친절한 편은 아니라서... 어느 정도는 코드를 읽을 줄 알아야 사용하기 쉬울 듯

근데 진짜 기초적인 것만 알아도 바로 사용 가능한 수준이라서 매우 추천합니다

에셋스토어 리뷰 보면 별점 1점주면서 뭐라고 써놓은사람 많은데(대체로 튜토리얼 문서나 성능 관련)

 

일단 나는 문서 없어도 샘플 코드 보고 사용법을 알 수 있었고,

그렇게 큰 게임이 아니라서 성능 관련해서도 딱히 문제가 생길 것 같진 않았다

그래서 그냥 사용함 ㅎㅎ

 

참고로 혹시나 검색해서 오는 사람이 있을까 싶어서 기능 설명을 해두자면

using UnityEngine;
using UnityEngine.UI;
using Hellmade.Sound;

public class SoundManager : MonoBehaviour
{
    public static SoundManager instance;

    public EazySoundDemoAudioControls[] SFXControls;// SFX 컨트롤 저장
    public EazySoundDemoAudioControls[] AudioControls; // 브금 컨트롤 저장
    public Slider globalVolSlider; //전체볼륨
    public Slider globalMusicVolSlider; // 배경음악
    public Slider globalSoundVolSlider; // SFX
    public AudioSource audioSource;

    void Awake()
    {
        // Singleton pattern
        if (instance == null)
        {
            instance = this;lad
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }

        // 슬라이더 초기화
        GlobalVolumeChanged();
        GlobalMusicVolumeChanged();
        GlobalSoundVolumeChanged();

        PlayBGM(0);
    }


    // SFX 출력합니다.
    public void PlaySFX(int sfxId)
    {
        EazySoundDemoAudioControls audioControl = SFXControls[sfxId];
        int audioID = EazySoundManager.PlaySound(audioControl.audioclip, audioControl.volumeSlider.value);

        SFXControls[sfxId].audio = EazySoundManager.GetAudio(audioID);
    }

    // 브금 출력 관리
    public void PlayBGM(int bgmId)
    {
        EazySoundDemoAudioControls audioControl = AudioControls[bgmId];
        int audioID = EazySoundManager.PlayMusic(audioControl.audioclip, audioControl.volumeSlider.value, true, false);
    }


    // 볼륨 변경 메서드
    public void GlobalVolumeChanged() // 전체 볼륨 변경
    {
        EazySoundManager.GlobalVolume = (globalVolSlider.value / 6);
    }

    public void GlobalMusicVolumeChanged() // 배경음악 볼륨 변경
    {
        EazySoundManager.GlobalMusicVolume = (globalMusicVolSlider.value / 6);
    }

    public void GlobalSoundVolumeChanged() // SFX 볼륨 변경
    {
        EazySoundManager.GlobalSoundsVolume = (globalSoundVolSlider.value / 6);
    }

}

 

이런식으로 사운드매니저는 따로 만들어줘야 하는 것 같았다

볼륨변경은 슬라이더에 값 변경시 볼륨변경 메서드를 연결해주면 되고

사운드 출력은.. 나는 따로 List 만들어서 Inspector에서 사용할 사운드 넣은 다음에 메서드로 실행시켰음

 

요로코롬

 

스크립트로 실행시킬 때도 PlayBGM(0); 이런식으로 실행시켜주면 되니 매우 간단

쉽죠잉?

 

 

2. Feel

아 이거 진짜좋음

얼마나 좋냐면, 진짜 좋습니다

진짜 추천

 

 

별별 기능이 다 들어가있다

오브젝트 이동, Active/Inactive 관리, 화면 쉐이크, 이미지 알파값 조정 텍스트 색상 조정 아 아무튼 다 들어가있음

들어가있는 기능은 아래 링크 들어가면 볼 수 있음

https://feel.moremountains.com/

(스크롤 좀 내려서 Demo에서 플레이 해볼 수 있음요)

 

공식문서도 정리 꽤 잘 되어있으니까

이건 어떻게 쓰는거지? 하는 부분 있으면 공식문서 읽으면 뚝딱이다

 

근데 아무리 찾아봐도 이미지 변경 기능은 없는 것 같아서?..

그건 내가 그냥 코드를 수정해버렸음.........

허허..;

protected override void CustomPlayFeedback(Vector3 position, float feedbacksIntensity = 1.0f)
{
    if (!Active || !FeedbackTypeAuthorized)
    {
        return;
    }

    if (EnableOnPlay)
    {
        Turn(true);
    }

    if (NewSprite != null)
    {
        BoundImage.sprite = NewSprite;
    }

    switch (Mode)
    {
        case Modes.Instant:
            if (ModifyColor)
            {
                BoundImage.color = InstantColor;
            }
            break;
        case Modes.OverTime:
            if (!AllowAdditivePlays && (_coroutine != null))
            {
                return;
            }
            _coroutine = Owner.StartCoroutine(ImageSequence());
            break;
    }
}

 

이렇게 추가해줘서 이미지 교체 기능 구현한 듯?

너무 오래전에 코드 쓴거라서 기억이 잘 안난다...

전체 코드는 아래 접은글로..

 

더보기

MMF_Image.CS

using System.Collections;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;

namespace MoreMountains.Feedbacks
{
    /// <summary>
    /// This feedback will let you change the color of a target sprite renderer over time, and flip it on X or Y. You can also use it to command one or many MMSpriteRendererShakers.
    /// </summary>
    [AddComponentMenu("")]
    [FeedbackHelp("This feedback will let you change the color of a target Image over time. You can also use it to command one or many MMImageShakers.")]
    [FeedbackPath("UI/Image")]
    public class MMF_Image : MMF_Feedback
    {
        /// a static bool used to disable all feedbacks of this type at once
        public static bool FeedbackTypeAuthorized = true;
        /// sets the inspector color for this feedback
#if UNITY_EDITOR
        public override Color FeedbackColor { get { return MMFeedbacksInspectorColors.UIColor; } }
        public override bool EvaluateRequiresSetup() { return (BoundImage == null); }
        public override string RequiredTargetText { get { return BoundImage != null ? BoundImage.name : ""; } }
        public override string RequiresSetupText { get { return "This feedback requires that a BoundImage be set to be able to work properly. You can set one below."; } }
#endif

        /// the duration of this feedback is the duration of the Image, or 0 if instant
        public override float FeedbackDuration { get { return (Mode == Modes.Instant) ? 0f : ApplyTimeMultiplier(Duration); } set { Duration = value; } }
        public override bool HasChannel => true;
        public override bool HasAutomatedTargetAcquisition => true;
        protected override void AutomateTargetAcquisition() => BoundImage = FindAutomatedTarget<Image>();

        /// the possible modes for this feedback
        public enum Modes { OverTime, Instant }

        [MMFInspectorGroup("Image", true, 54, true)]
        /// the Image to affect when playing the feedback
        [Tooltip("the Image to affect when playing the feedback")]
        public Image BoundImage;
        /// the new sprite to set on the Image
        [Tooltip("the new sprite to set on the Image")]
        public Sprite NewSprite;

        /// whether the feedback should affect the Image instantly or over a period of time
        [Tooltip("whether the feedback should affect the Image instantly or over a period of time")]
        public Modes Mode = Modes.OverTime;
        /// how long the Image should change over time
        [Tooltip("how long the Image should change over time")]
        [MMFEnumCondition("Mode", (int)Modes.OverTime)]
        public float Duration = 0.2f;
        /// if this is true, calling that feedback will trigger it, even if it's in progress. If it's false, it'll prevent any new Play until the current one is over
        [Tooltip("if this is true, calling that feedback will trigger it, even if it's in progress. If it's false, it'll prevent any new Play until the current one is over")]
        public bool AllowAdditivePlays = false;
        /// whether or not to modify the color of the image
        [Tooltip("whether or not to modify the color of the image")]
        public bool ModifyColor = true;
        /// the colors to apply to the Image over time
        [Tooltip("the colors to apply to the Image over time")]
        [MMFEnumCondition("Mode", (int)Modes.OverTime)]
        public Gradient ColorOverTime;
        /// the color to move to in instant mode
        [Tooltip("the color to move to in instant mode")]
        [MMFEnumCondition("Mode", (int)Modes.Instant)]
        public Color InstantColor;
        /// whether or not that Image should be turned off on start
        [Tooltip("whether or not that Image should be turned off on start")]
        [FormerlySerializedAs("StartsOff")]
        public bool DisableOnInit = false;
        /// if this is true, the target will be enabled when this feedback gets played
        [Tooltip("if this is true, the target will be enabled when this feedback gets played")]
        public bool EnableOnPlay = true;
        /// if this is true, the target disabled after the color over time change ends
        [Tooltip("if this is true, the target disabled after the color over time change ends")]
        public bool DisableOnSequenceEnd = true;
        /// if this is true, the target will be disabled when this feedbacks is stopped
        [Tooltip("if this is true, the target will be disabled when this feedbacks is stopped")]
        public bool DisableOnStop = true;

        protected Coroutine _coroutine;
        protected Color _initialColor;
        protected Sprite _initialSprite;

        /// <summary>
        /// On init we turn the Image off if needed
        /// </summary>
        /// <param name="owner"></param>
        protected override void CustomInitialization(MMF_Player owner)
        {
            base.CustomInitialization(owner);

            if (Active)
            {
                _initialColor = BoundImage.color;
                _initialSprite = BoundImage.sprite;

                if (DisableOnInit)
                {
                    Turn(false);
                }
            }
        }

        /// <summary>
        /// On Play we turn our Image on and start an over time coroutine if needed
        /// </summary>
        /// <param name="position"></param>
        /// <param name="feedbacksIntensity"></param>
        protected override void CustomPlayFeedback(Vector3 position, float feedbacksIntensity = 1.0f)
        {
            if (!Active || !FeedbackTypeAuthorized)
            {
                return;
            }

            if (EnableOnPlay)
            {
                Turn(true);
            }

            if (NewSprite != null)
            {
                BoundImage.sprite = NewSprite;
            }

            switch (Mode)
            {
                case Modes.Instant:
                    if (ModifyColor)
                    {
                        BoundImage.color = InstantColor;
                    }
                    break;
                case Modes.OverTime:
                    if (!AllowAdditivePlays && (_coroutine != null))
                    {
                        return;
                    }
                    _coroutine = Owner.StartCoroutine(ImageSequence());
                    break;
            }
        }

        /// <summary>
        /// This coroutine will modify the values on the Image
        /// </summary>
        /// <returns></returns>
        protected virtual IEnumerator ImageSequence()
        {
            float journey = NormalPlayDirection ? 0f : FeedbackDuration;

            IsPlaying = true;
            while ((journey >= 0) && (journey <= FeedbackDuration) && (FeedbackDuration > 0))
            {
                float remappedTime = MMFeedbacksHelpers.Remap(journey, 0f, FeedbackDuration, 0f, 1f);

                SetImageValues(remappedTime);

                journey += NormalPlayDirection ? FeedbackDeltaTime : -FeedbackDeltaTime;
                yield return null;
            }
            SetImageValues(FinalNormalizedTime);
            if (DisableOnSequenceEnd)
            {
                Turn(false);
            }
            IsPlaying = false;
            _coroutine = null;
            yield return null;
        }

        /// <summary>
        /// Sets the various values on the sprite renderer on a specified time (between 0 and 1)
        /// </summary>
        /// <param name="time"></param>
        protected virtual void SetImageValues(float time)
        {
            if (ModifyColor)
            {
                BoundImage.color = ColorOverTime.Evaluate(time);
            }
        }

        /// <summary>
        /// Turns the sprite renderer off on stop
        /// </summary>
        /// <param name="position"></param>
        /// <param name="feedbacksIntensity"></param>
        protected override void CustomStopFeedback(Vector3 position, float feedbacksIntensity = 1)
        {
            if (!Active || !FeedbackTypeAuthorized)
            {
                return;
            }
            IsPlaying = false;
            base.CustomStopFeedback(position, feedbacksIntensity);
            if (Active && DisableOnStop)
            {
                Turn(false);
            }
            _coroutine = null;
        }

        /// <summary>
        /// Turns the sprite renderer on or off
        /// </summary>
        /// <param name="status"></param>
        protected virtual void Turn(bool status)
        {
            BoundImage.gameObject.SetActive(status);
            BoundImage.enabled = status;
        }

        /// <summary>
        /// On restore, we restore our initial state
        /// </summary>
        protected override void CustomRestoreInitialValues()
        {
            if (!Active || !FeedbackTypeAuthorized)
            {
                return;
            }
            BoundImage.color = _initialColor;
            BoundImage.sprite = _initialSprite;
        }
    }
}

아무튼 유용한 기능이 지이이이이이이이이이인짜 많고

또 정말 애용하고 있는 에셋이라서

정말 추천합니다.

에셋스토어 별점 1~2점 리뷰가 0개임 엌ㅋㅋㅋㅋㅋㅋ

Feel 에셋의 위엄이 느껴지십니까? 

 

 

 

3. Text Animator for Unity

 

이거 진.짜.귀.엽.습.니.다.

기능은 간단함

textmeshpro로 작성한 텍스트를 움직이게 만든다

끝..

 

참 별거 없어보이는데 텍스트 애니메이션이 있고 없고 차이가 좀 커서..

그리고 일단 글자가 움직이잖아.

귀엽잔아.

 

이미지에 없는 기능도 있음

 

이런 걸 할 수 있다

전 로딩화면 로딩중.. 텍스트로 이걸로 해결했습니다

사용방법도 간단해서 그냥  html 쓰듯이 태그로 사용하면 됨

위글 효과를 주고싶다면 <wiggle>위글위글</wiggle> 이런 방식..

 

다들 장미가족의 태그교실 이런곳에서 html 써본 적 잇을거잖아요?

없음?

축하드립니다. 당신은 알파세대입니다.

 

 

이거 말고도

Asset Cleaner라는 에셋도 샀는데 이건 나중에 후기 남겨야지...

아직 안 써봐서 좋다 안좋다 평을 못 하게슴

그 외에도 Kawaii UI 라는 SFX 사운드 에셋이 있는데...

이거 너무 사고싶은데 너무너무 비싸서 못 샀다..

약간 닌텐도 스위치스러운 감성의 SFX 사운드임 흐흐

 

 

 

아무튼 오늘 글의 본 목적 2

~채찍피티에 대한 이야기 편~

 

 

복학하고나서 챗지피티라는 엄청난 신문물이 등장해서 애용중인데

나도 채찍피티의 매력에 빠져버려서..

무려 GPT4 를 유료구독 하고 있는 중이다

아무튼 이번 게임을 제작하면서 챗지피티랑 놀았던 거? 기록을 좀 해볼까 한다

 

1. 지피티의 이미지 인식

 

지피티한테 이런식으로 이미지 주면서 레이아웃 짜달라고 하면 짜준다...

근데 예시 이미지처럼 좀 알아보기 힘들게(텍스트 인식이 안되거나 하는 경우) 되어있으면 이상하게 만들어주고

깔끔한 이미지 주면 잘 만들어줬음

 

2. 지피티는 레벨디자인도 가능하다...?

 

내가 레벨디자인은 아예 모르는데다가

특히 클리커류 게임에서의 재미는 거의 90%정도를

레벨디자인이 차지하고 있다고 생각하기 때문에...

나보다는 지피티를 믿는게 2억배 나을 것 같아서 맡겼는데

의외로 괜찮은 결과가 나왔..나?

 

근데 역시나 레벨디자인도 기본적인 정보는 알려줘야지 잘 알아듣는 것 같았음

 

 

3. 챗지피티의 가능성??

 

확실히 자료수집이나 리서치 같은 부분에서는

채찍피티가 믿을 만한 놈이 못 되는 반면에

코딩은 사용할 가치가 있는 것 같음...?

물론 취직하면 못 쓰겠지만..... (코드 유출? 같은 거 때문에 안된대요!!)

 

근데 아예 노베이스로 챗지피티만을 사용해서 코딩해야지! 는

불가능의 영역에 가까운 것 같고(천재적인 재능이 있다면 가능할지도..?)

코드 읽는 법처럼 기본적인 skill은 거의 반 필수적인 것 같음.

 

기능 A를 구현하기 위해서 필요한 로직의 뼈대를 생각해낼 수 있다면

챗지피티만 사용해서 개발하는 건 완전 쌉가능의 영역인 것 같음..

우선 나도 지금 그렇게 개발하고있고.. 이번 방학에는 백준 정권지르기 진짜 한다

 

4. 결론

 

결론적으로 챗지피티의 사용도 어느정도는 코딩의 영역(ㅋㅋ) 에 있는 것 같음.

프롬프트 작성을 제대로 하지 않으면 CGPT가 내 명령을 제대로 알아듣지 못하고 이상한 결과를 출력하기 때문에

프롬프트를 제대로 입력하는 것이 매우매우매우 중요했던 듯???

 

추가로 챗지피티만을 사용해서 개발할 예정이 있거나 하면 CGPT4 결제는 반쯤 필수일 것 같음. 

무료버전도 애용했었는데 유료 결제를 한 GPT는 아예 성능부터가 다른 것 같음....

애초에 무료버전은 한 번에 보낼 수 있는 텍스트의 양이 제한되어 있는 것 같아서?

코드 첨삭 부탁하기 좀 애매했던 것 같으이

 

 

 

 

 

 

 

계속 수정해서 베타테스트까지 돌려볼 즈음이 된다면 코드리뷰도 해봐야겠다

myoskin