This privacy policy applies to the 미고미고 app (hereby referred to as "Application") for mobile devices that was created by (hereby referred to as "Service Provider") as a Free service. This service is intended for use "AS IS".
Information Collection and Use
The Application collects information when you download and use it. This information may include information such as
Your device's Internet Protocol address (e.g. IP address)
The pages of the Application that you visit, the time and date of your visit, the time spent on those pages
The time spent on the Application
The operating system you use on your mobile device
The Application does not gather precise information about the location of your mobile device.
The Application collects your device's location, which helps the Service Provider determine your approximate geographical location and make use of in below ways:
Geolocation Services: The Service Provider utilizes location data to provide features such as personalized content, relevant recommendations, and location-based services.
Analytics and Improvements: Aggregated and anonymized location data helps the Service Provider to analyze user behavior, identify trends, and improve the overall performance and functionality of the Application.
Third-Party Services: Periodically, the Service Provider may transmit anonymized location data to external services. These services assist them in enhancing the Application and optimizing their offerings.
The Service Provider may use the information you provided to contact you from time to time to provide you with important information, required notices and marketing promotions.
For a better experience, while using the Application, the Service Provider may require you to provide us with certain personally identifiable information. The information that the Service Provider request will be retained by them and used as described in this privacy policy.
Third Party Access
Only aggregated, anonymized data is periodically transmitted to external services to aid the Service Provider in improving the Application and their service. The Service Provider may share your information with third parties in the ways that are described in this privacy statement.
Please note that the Application utilizes third-party services that have their own Privacy Policy about handling data. Below are the links to the Privacy Policy of the third-party service providers used by the Application:
The Service Provider may disclose User Provided and Automatically Collected Information:
as required by law, such as to comply with a subpoena, or similar legal process;
when they believe in good faith that disclosure is necessary to protect their rights, protect your safety or the safety of others, investigate fraud, or respond to a government request;
with their trusted services providers who work on their behalf, do not have an independent use of the information we disclose to them, and have agreed to adhere to the rules set forth in this privacy statement.
Opt-Out Rights
You can stop all collection of information by the Application easily by uninstalling it. You may use the standard uninstall processes as may be available as part of your mobile device or via the mobile application marketplace or network.
Data Retention Policy
The Service Provider will retain User Provided data for as long as you use the Application and for a reasonable time thereafter. If you'd like them to delete User Provided Data that you have provided via the Application, please contact them at aa01092308481@gmail.com and they will respond in a reasonable time.
Children
The Service Provider does not use the Application to knowingly solicit data from or market to children under the age of 13.
The Application does not address anyone under the age of 13. The Service Provider does not knowingly collect personally identifiable information from children under 13 years of age. In the case the Service Provider discover that a child under 13 has provided personal information, the Service Provider will immediately delete this from their servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact the Service Provider (aa01092308481@gmail.com) so that they will be able to take the necessary actions.
Security
The Service Provider is concerned about safeguarding the confidentiality of your information. The Service Provider provides physical, electronic, and procedural safeguards to protect information the Service Provider processes and maintains.
Changes
This Privacy Policy may be updated from time to time for any reason. The Service Provider will notify you of any changes to the Privacy Policy by updating this page with the new Privacy Policy. You are advised to consult this Privacy Policy regularly for any changes, as continued use is deemed approval of all changes.
This privacy policy is effective as of 2024-04-21
Your Consent
By using the Application, you are consenting to the processing of your information as set forth in this Privacy Policy now and as amended by us.
Contact Us
If you have any questions regarding privacy while using the Application, or have questions about the practices, please contact the Service Provider via email at aa01092308481@gmail.com.
3D Sci-fi 분위기의 탄막슈팅게임(주 레퍼런스: 아머드코어)을 반쯤 순조롭게 제작 진행중. 나는 지금까지 거의 탄막을 제작한 게 다 이다. 이제 대충 양산을 할 수 있는 상태가 되기는 했지만 여전히 부족한 게 많은 상태. 특히 코드가 무척 스파게티 상태라서 리팩토링을 하고 싶다. 탄막 작업이 끝나면, 업적 시스템에 착수해야 한다.
누가봐도 최적화 문제 + 왠지 씬에 남아있는 투명한 탄막 + 뭔가 시간이 지날수록 점점 무거워지는 게 무엇인가가 메모리에 누적되어가는 것 같은 문제
0.3 FPS (3035.8ms)를 보이며 도저히 게임을 진행할 수 없는 상태로 보인다. (20만 배치수가 무척 심각한 상태라는 것은 이슈를 해결하고 나서야 알았다)
영상으로 보면 아래와 같다.
이후 탄막의 렌더러를 없애봐도 비슷한 타이밍에 fps가 나락가는 걸 보면, 정말 단순하게 연산쪽이 무거워서 그런 것으로 보인다(코드가 지저분해서 그럴 만 하다고 생각했다)
버그 픽스를 위해
오브젝트 풀에서 초기화 또는 반환 도중 에러가 난 아이들
원점에 돌아갔지만, 움직이지 않고 Update문을 반복하며 오류를 뿜는다.
초기화 오류인가, 반환 오류인가?
수동으로 반환 메서드를 실행하면 잘 되며, 여러가지 초기화 해 주어야 할 값이 모두 null인 것으로 보아, 초기화가 제대로 이루어지지 않은 것으로 보인다. 즉 DanmakuGenerator에서 미스가 있다.
var danmakuGo = DanmakuPoolManager.instance.GetGo(settings.danmakuPrefab.name);
try
{
if (danmakuGo != null)
{
Vector3 initPosition = masterTransform.position; // 마스터의 위치를 기본값으로
// ... 기타 로직 및 탄막 파라미터들 초기화
}
}
catch
{
Pool.Release(danmakuGo);
}
아무래도 로직 도중 masterObject가 소실되어 Pool에서의 탄막 생성 후 이후 로직이 모두 무산되고 danmakuController.Initialize도 실행되지 않은 것으로 보인다. 해당 로직을 try문으로 감싸, 초기화가 제대로 이루어지지 않을 경우 바로 Pool에 반환을 할 수 있도록 해야겠다.
var danmakuGo = DanmakuPoolManager.instance.GetGo(settings.danmakuPrefab.name);
try
{
if (danmakuGo != null)
{
Vector3 initPosition = masterTransform.position; // 마스터의 위치를 기본값으로
// ... 기타 로직 및 탄막 파라미터들 초기화
}
}
catch
{
Pool.Release(danmakuGo);
}
해결되지 않았다. 아직 활성화 직전인 오브젝트를 반환 할 수 없는 게 이유인 것으로 보인다. 새로운 탄막 오브젝트를 Pool에서 꺼내오기 전에 masterObject의 상태를 체크하여 코루틴을 종료 할 수 있도록 해 보았다.
// masterObject의 상태 확인
if (masterObject == null || !masterObject.activeInHierarchy)
{
yield break; // masterObject가 비활성화되거나 파괴되면 코루틴 중단
}
var danmakuGo = DanmakuPoolManager.instance.GetGo(settings.danmakuPrefab.name);
해결 된 듯 보여 탄막 오브젝트를 1.6^4 = 6.6배정도 증량하여 더 부하를 줘 보았다.
FPS가 1.0 이하로 떨어지고 탄막의 반환이 살짝 늦을지언정 더 이상 Pool 버그는 일어나지 않았다.
참고로 위 영상에서 최대 탄막수는 4096개였다. Pool의 크기는 100였기 때문에, 사실상 Instantiate와 Destroy가 커버를 친 셈.
번외로 Pool의 크기를 4100으로 하여 FPS를 확인 해 보자. 버그를 찾으며 프로파일러를 확인하면서, Destory 등이 CPU성능이 90프로 이상인 시점이 있었던 것도 생각하면 왠지 FPS 저하가 플레이에 지장이 없을 정도가 될 것이라고도 생각된다.
워메… 아니었다. 프로파일러를 켜고 다시 확인 해 보자.
일단 랜더러쪽은 전혀 문제가 되지 않았고, 정말 그냥 Pool 반환을 위한 Deactive 량이 너무 많은 것이 원인이었다.(1.6초에 4000회) 그런데 이 반환 처리가 생성파괴와 성능차이가 크게 나지 않는다는 건 생각보다 쇼크.
생명주기 시간을 좀 늘려 반환이 이루어지지 않는 시점에도 FPS가 많이 떨어지는지 확인 해 보아야겠다. 실제 게임에서는 반환이 이렇게 짧은 시간에 대량으로 몰려있지는 않을 것으로 예상되기 때문에.
생성이 갑작스럽게 한꺼번에 이루어져 7.7 FPS를 가지긴 했지만, 이후 약 5000개의 탄막 오브젝트가 이동하는 데에는 11.5 FPS부터 시작하여 약 20 FPS까지 올라간다.
생각 해 보니 하이에라키 등 게임 씬 이외의 요소가 성능저하의 문제가 될 가능성이 있어, Play Maximized 모드에서 추가로 확인을 해 보았다.
생성순간이 13 FPS, 이후 18 FPS부터 시작하여 카메라에서 탄막이 사라지면서(낮아지는 Batches 수) 30 FPS 이상까지 회복한다. Play Maximized 모드에서 대략 1.5배정도의 성능을 보였다.
보스몬스터가 탄막을 무척 남발하도록 하고 싶은데, 이런 상태라면 좀 어려울 것으로 생각된다. 최적화가 필요한 부분이다.
다만, 현재 최우선순위는 패턴의 다양화이고, 이후 탄막의 네온 효과, 카메라에 포스트 프로세서등을 달아 어느 정도의 최적화가 더 필요한지 확인하여 견적을 잡아야겠다.
3. 과제에 대해
탄막의 네온효과, 카메라의 포스트 프로세싱 적용, 실제 게임 중 탄막 사용 예상량 등을 근거로 프레임 저하가 어느 정도 더 일어나는지 확인한다. 이후 견적을 내어 필요한 만큼의 최적화를 진행. 다만 후순위 작업이 될 것이고, 패턴의 다양화를 위한 추가 스크립팅을 하는 것을 우선으로 한다.
Q 1. 자기소개 해주세요 Q 2. 게임개발을 하게 된 이유는 Q 3. 유니티 생명주기에 대해 설명해주세요 Q 4. 객체지향에 대해 설명해주세요 Q 5. 상속에 대해 설명해주세요
대답
대학 졸업하고 이것저것 프밍공부 하고 게임개발 공부중.. 어버버
게임을 하는 것도 좋아하기도 하고, 비쥬얼적으로 뭔갈 만들어서 보여주는 것에 특화되어있고 게임 뿐 아니라 여러 시뮬레이션을 만들어 보는 것에 대해서도 유니티가 적합하다고 생각하여 열심히 배우고 있음. 여기도 어버버
게임 시작 시점에 실행되는 무언가. [] 대괄호로 둘러싼 키워드와 함께 하는 그거. 이후에 Awake, OnEnable, Start 등 오브젝트 컴포넌트의 활성화 단계에서 한 번 메서드를 실행하여 여러 초기화 역할을 하며, Update 메서드 등에서 프레임 당 필요한 작업을 한다. (FixedUpdate나 콜백 등에 대해서는 빼먹음) 오브젝트의 파괴 시 OnDistroy 메서드에서 필요한 장치를 마련하여 마무리.
순차적으로 진행되는 절차지향과는 다른 결의 프로그래밍 방식으로, 모듈화를 중심으로 프로그래밍을 하는 방식을 객체지향 프로그래밍이라고 한다.
특정 클래스에 어떠한 기능을 만들어 이를 다른 클래스에서 이어받아 추가적인 메서드나 변수를 작성하여 사용 할 수 있는 구조. 몬스터를 예로 들면, 몬스터 클래스에 몬스터가 가지는 기능을 구현한 후, 보스 몬스터에서 몬스터 클래스를 상속받고 보스몬스터만의 추가적인 메서드 등을 구현하여 사용. 쫄 몬스터에서도 마찬가지로 몬스터 클래스를 상속받아 쫄 몬스터에 대한 메서드 등을 추가로 작성하여 사용한다.
피드백
나에 대한 소개 : 내가 어떤 사람이고, 내가 왜 게임개발을 하려고 하는지. 나라는 사람이 어떠한 사람인지 표현할 수 있도록 정리를 해 보자.
전반적으로 부족한 자신감 : 내가 부족하다고 생각하는 부분을 스스로 인지하게 되면 자신감이 떨어지는 경향이 있음.
본인소개부터 막혀서 거기에서 자신감이 떨어지고, 기술적인 부분에서도 설명을 하다가 막히기 시작하면 떨어지는 자신감.
대답을 하다가 흐리는 경향. "~게 한다고.....합니다.". "~라고 생각합니다" 는 지양하도록. "~게 됩니다."라고 깔끔하게 끝내기.
시선처리 : 흔들리는 시선
기술에 대한 대답도 정리가 필요.
AtoZ를 모두 말하는 것 보다 AtoZ를 모두 알고있다는 것을 요약.(예:생명주기)
요약 : 자신감 / 마인드(걔네는 면접하는 게 일) / 내가 생각하는 걸 잘 말할 수 있도록 연습
추후 실사용에서는 tag:Player를 사용하지는 않겠지만, 테스트를 위해서 플레이어 오브젝트를 만들고 플레이어 태그를 통해 위치정보를 알아내도록 세팅하였다.
1차적으로 선형 발사를 위한 PatternSO 변수 준비를 마쳤다.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Patterns", menuName = "DanmakuSO/Pattern", order = 0)]
public class PatternSO : ScriptableObject
{
public List<DanmakuPatternData> patternDatas;
// 패턴 이름을 통해 패턴 정보를 찾기 -> 추후 최적화를 위한 수정 필요. 어떻게 쓰일지도 살짝 애매모호한 상태.
// ▲ 현재 상태 : PhaseSO에서 patternName을 입력하여 저장, Enemy에서 'GetSpawnInfoByPatternName' 이용하여 탄막패턴 사용. 일단 OK
// TODO.
// But, 커스텀 고려해서 수정 필요.
// 그리고 GetSpawnInfoByPatternName 자체에 대해서도 연산 더 낮은 방식으로 바꾸어야 함.
public DanmakuPatternData GetSpawnInfoByPatternName(string patternName)
{
foreach (var patternData in patternDatas)
{
if (patternData.patternName == patternName)
{
return patternData;
}
}
return null;
}
}
[System.Serializable]
public class DanmakuPatternData
{
public string patternName;
public DanmakuSettings danmakuSettings;
}
[System.Serializable]
public struct DanmakuSettings // 추가 할 게 진짜 많다.. 트리 이미지로 {1.생성-2.이동-3.하층생성-4.반환} 명심하여 작성
{
// 아래의 내용은 전-부 PhaseSO 작성 시 커스텀이 가능. 다만 선택적 커스텀을 어떻게 해야할지 떠오르지가 않음. 커스텀 값을 기본적으로 모두 null로 둘 수 있을까?
// 1. 생성 ---------------------------------//------------------------------------------------------------------
// 1-1. 탄막의 모양
public GameObject danmakuPrefab; // 탄막 기본 프리팹
// 1-2. 생성 시간과 횟수에 관련된 정보
public float initDelay; // 첫 탄막 생성까지의 지연
public int numOfSet; // 총 세트 수
public float setDelay; // 세트 사이의 지연
public int shotPerSet; // 한 세트에서 탄막을 몇 차례 생성할지
public float shotDelay; // 탄막 생성 사이의 지연
// 1-3. 생성 모양에 관한 정보
// Memo. 여기서 할게 꽤 많음. 차차 작성. 03월 11일 목표는 여기! SO 정보를 읽어 구 모양으로 발사하기. 필수요소만 준비하여 시연을 해 보자.
// a. 어느 방향을 기준으로 생성을 시작할 것인지
public PosDirection posDirection; // 주체 기준으로 생성될 방향
public Vector3 customPosDirection; // > posDirection이 World일 경우 지정
// a-plug. 기준 방향에 랜덤성을 줄 것인지.
// public bool posDirectionHasRandomness; // 랜덤성 부여
// public PosDirectionRandomType posDirectionRandomType; // > 랜덤성이 직선인지, 평면인지. 이후에 고려할 사항도 다수
// >a> 랜덤성이 직선일 경우, 그 직선의 형태
// >a> 랜덤성이 직선일 경우, 그 직선의 범위 또는 양 방향 각각의 범위(각도가 될 듯)
// >b> 랜덤성이 평면일 경우, 그 평면의 형태. 능력이 안된다면, 아래의 조건만 보아 콘 형태부터.
// >b> 랜덤성이 평면일 경우, 허용 범위. 즉 기준점으로부터의 허용 각도
// b. 기준방향을 중심으로 어떤 형태의 방사를 사용할지. 거리와 방향을 포함.
// 간단한 선형 단일 발사부터, 정육면체 모양으로 속도를 달리 한 발사, 특별한 모양으로 생성되어 각각이 랜덤한 타이밍에 발사 등 다양한 형태.
// b-1. 형태에 관해. 기본적인 프리셋을 제공하되, 유저가 Vector3를 직접 작성하여 입력할 수 있도록도 하자.
public DanmakuShape danmakuShape; // 탄막 모양의 타입
// b-2. 거의 모든 모양에서 사용할 변수들
public float initDistance; // 모든 탄막에 대한 생성거리의 기준
// 기준 거리에 대한 랜덤성 부여. 여유가 되면 작성.
// 이 랜덤성을, 모든 탄막에 동일부여할지, 각 탄막에 따로 부여할지의 여부.
public int numPerShot; // 한번 발사에 사용되는 탄막 갯수.
// 참고: 일부 Shape들()에 대해서는 numPerShot으로 해결이 되기 때문에 이러한 형태들은 b-3항목 불필요.
// b-3. 탄막 모양에 따라 선택적 변수들(이후, 조건부로 Inspector에 보여주는 것이 과제)
public float shotVerticalDistance; // Circle: 원의 면과 보스의 수직거리
public int shotVerticalNum; // Sphere: 구의 '단' 갯수
// Cone: 허용각도. 얘는 자료형을 뭘로 해야할지 모르겠음.
// 전체 모양의 회전을 틀어버릴 요소(정해진 값)
// 전체 모양의 회전을 틀어버릴 값의 랜덤 여부. true라면 위 값을 범위로 사용. // 이 두 랜덤변수는 a-plug에서 커버 가능한 부분으로 보임. 삭제 예정
// b-99. 유저 커스텀 입력
// 유저입력1. 원하는 범위에 a.N개를 균일배치(어려울듯), b.N개를 랜덤배치
// 원하는 범위는... 일단 x, y, z의 범위? 이것만으로는 마음엔 들진 않을 듯.(이 방식으론 직육면체 밖에 불가능)
// 그래프의 형태로 입력받아 활용할 수 있을 것 같지만 난이도가 있을 듯 하다.
// 유저입력2. 완전한 위치들의 리스트를 전달받기
// 탄막의 방향 : 일단 주체기준으로 밖으로 퍼지도록 Outer로 설정하여 테스트
public DanmakuToDirection danmakuToDirection;
// 2. 이동 ---------------------------------//------------------------------------------------------------------
// 탄막 자체의 세팅
public float initSpeed; // 시작속도. 일단은 정속으로 테스트, 추후 수정.
public Vector3 initDirection; // 시작방향. 일단 보는방향으로 테스트, 추후 수정.
// 변속정보.
// 변향정보. 플레이어에 유도 등 여러가지 요인으로 변수 추가 가능성 높음.
// 변속정보(불연속) 리스트
// 변향정보(불연속) 리스트
// 나중에 필요하면 이어서 추가
// 3. 클론 ---------------------------------//------------------------------------------------------------------
public NextPatternMethod nextPatternMethod; // 하위 탄막을 생성의 조건.
// 타이머일 경우, 그 시간
// UserTrigger일 경우, 이벤트 구독
// 마찬가지로 테스트 이후 필요한만큼 추가
// 4. 반환 ---------------------------------//------------------------------------------------------------------
public ReleaseMethod releaseMethod; // Pool 반환의 조건.
public float releaseTimer; // 방법1. 반환까지의 타이머. 일단 이거로 테스트.
// 방법2. 충돌체크. Ground를 만날 시 반환 여부. 이벤트 감지 로직은 각 탄막에서보다 Ground에서 작성하는 것이 자원을 아낄 수 있을 것으로 보임.
// 방법3. 하위 탄막의 모든 세트 생성을 끝마친 경우
// 방법4. 주체의 트리거(구독)
// 그리고 이 모든것을 하나의 뭉치로하여, 하위 탄막에 전해주거나 할 것으로 일단 보임.
// 하위 탄막에 전해줄 내용 : 뭉탱이.
// 하위 탄막이 뭉탱이를 언패킹하여, 위의 내용을 모두 적용, 하위 뭉탱이가 있으면 이를 반복.
}
public enum PosDirection
{
World, // 주체 또는 플레이어의 방향과 무관계한
Look, // 주체가 바라보는
LookPlayer, // 주체가 플레이어를 바라볼 경우
CompletelyRandom, // 완전히 랜덤한 방향으로
}
public enum PosDirectionRandomType
{
Line, // 직선 범위에서 랜덤
Plane // 평면 범위에서 랜덤
}
public enum DanmakuToDirection
{
World, // 탄막의 방향과 무관계한
Outer, // 주체와 반대방향
LookPlayer, // 탄막이 플레이어를 바라보도록
CompletelyRandom, // 완전히 랜덤한 방향으로
}
public enum NextPatternMethod
{
Timer, // 특정 시간 뒤 터뜨리기
WithRelease, // 반환과 함께 터뜨리기(삭제예정)
UserTrigger, // Manager에서 트리거 관리. 트리거 작동 시, 구독한 탄막들 터뜨리기.
}
public enum ReleaseMethod
{
Timer, // 특정 시간 뒤 터뜨리기
WithRelease, // 반환과 함께 터뜨리기(삭제예정)
UserTrigger, // Manager에서 트리거 관리. 트리거 작동 시, 구독한 탄막들 터뜨리기.
}
public enum DanmakuShape
{
Linear, // 가장 단순한 선형 사출
Circle, // 원형 (참고: 플레이어를 본 방향으로 원형으로 만들어 Enemy->Player 벡터로 방향을 주거나, 무작위 방향으로 원형 바깥방향으로 사출하면 자연스러울 듯.)
Sphere, // 구형
Cube, // 큐브형태. (참고: 레퍼런스 있음)
Custom, // 유저 입력을 받아 모양을 커스텀. Vector3리스트의 깡 입력으로 여러가지 모양을 만들 수도 있도록.
}
해당 SO 정보를 읽어, Enemy가 SO세팅에 맞춘 공격을 할 수 있도록 해야겠다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Phases", menuName = "DanmakuSO/Phase", order = 1)]
public class PhaseSO : ScriptableObject
{
[System.Serializable]
public class PatternHierarchy
{
public PatternSO patternSO; // 참조할 PatternSO
public string patternName; // 실행할 패턴의 이름. PatternSO 에서
public float startTime; // 사이클 중 패턴이 시작할 시간
public float cycleTime; // 하위 패턴들을 굴릴 사이클의 전체 시간
public List<PatternHierarchy> subPatterns; // 하위 패턴 목록
}
public float cycleTime; // 하위 패턴들을 굴릴 사이클의 전체 시간
public List<PatternHierarchy> hierarchicalPatterns; // 계층 구조
// Todo.
// 여기 구조 다시 한 번 생각 해 봐야함. PatternSO를 참조하기보다 긁어와서 커스텀 할 수 있어야 할 것.
// PatternSO는 프리셋 모음이라는 것을 다시 한 번 상기하기
}
PhaseSO에서 PatternSO의 요소를 재귀적으로 아이템을 가질 수 있도록 구성하였다.
현재까지의 진행상황에 더불어, 파라미터를 제대로 전달하기 위한 장치를 구현해야한다.
Pool에 등록된 탄막의 DanmakuController.cs에서 OnEnable사용에 인해 예견된 문제 발생
아직 관련된 파라미터도 갖고있지 않으면서, 게임시작 시 한 차례 실행을 시도하여 오류가 발생한다. 추후 작업을 통해 해결을 해야 할 듯 하다.