Exit Time이란 특정 State에 진입했을 시, Transition 조건을 충족했더라도 다음 State로 넘어가지 않은 채로 버티는 시간을 말한다. 단위는 (Animation Clip의 재생 시간에 기반한) normalized이다.
주로 격투 게임에서 State Delay를 구현하는 데 사용된다. (예 : 땅에 착지하는 모션이 모두 재생된 다음에야 파동권을 쏠 수 있도록 만들기 위해 사용됨)
Fixed Duration
Transition Duration의 단위를 결정한다; "실제 초(fixed)"로 할 것인지, "normalized(%)"로 할 것인지?
Transition Duration
현재 State와 다음 State를 얼마만큼 Blend하여 Transition할 것인지 결정한다. 범위는 0 ~ 1까지이다. 이 값이 크면 클수록 자연스럽다. 그러나 너무 크면 Input과 Action의 딜레이가 크다는 느낌을 받을 수도 있으니, 0.2 ~ 0.25 정도로만 정하자.
참고로 Sprite Sheet Animation이라면 무조건 0으로 설정해야 한다. 자연스러운 Blend가 불가능하다.
Transition Offset
애니메이션을 어느 시점에서부터 재생할지 결정하는 옵션이다.
대표적인 사용처로는 Idle -> Walk Transition에서 Walk State의 Transition Offset을 25%로 설정하는 것이다. 그러면 발이 순간이동하는 것처럼 보이는 문제를 해결할 수 있다.
현재 State를 받는 법
AnimatorStateInfo를 통해 받을 수 있다.
사용 예 : Update() 내부에 AnimatorStateInfo stateinfo_base = anim.GetCurrentAnimatorStateInfo(0)를 넣는다.
참고로 GetCurrentAnimatorStateInfo(int)에서 int는 레이어를 넣는 곳이다. 3D Rig Humanoid Animation에서 자주 사용되는 개념으로 0이면 Base, 1이면 Upper Body이다.
보통 Edit > Project Settings > Player에서 많이 한다. 후술할 항목들도 이곳에서 설정값을 만진다는 가정 하에서만 유효하다. 처음 Player 창에 들어가면 Company Name부터 시작해서 Icon, Resolution and Presentation, Splash Image, Other Settings가 보일 것이다.
기본 설정
Company Name : 제작자의 이름을 적는 곳
Product Name : 게임의 이름을 적는 곳
Version : 앱스토어에 표시될 버전을 적는 곳. 그러나 실제로 버전이 "달라졌다는" 사실은 다른 항목으로 판정한다.
Default Icon : 앱 아이콘
Resolution and Presentation
Orientation > Default Orientation에서 Auto Rotation으로.
Orientation > Allowed Orientation for Auto Rotation에서, 가로 게임이면 Landscape Right & Landscape Left만 체크. 세로 게임이면 Portrait & Portrait Upside Down만 체크.
Splash Image
Logos 리스트에 자신이 원하는 Logo를 추가할 수 있다.
Preview를 통해 취향껏 커스텀하자.
Unity 6.0 버전에서도 Unity Logo를 지울 수 있다.
Other Settings
Identification > Override Default Package Name 체크 해제. (이유 : Unity가 Company Name과 Product Name을 합쳐 자동 생성한 Package Name은 바꾸지 않는 것이 좋다. 이게 다르면 플랫폼이 아예 다른 앱으로 인식하고, 업데이트 대신 "새로 설치"를 종용하기 때문)
Identification > Bundel Version Code는 앱을 업데이트 할 때마다 1씩 늘려줘야 한다. (이유 : 플랫폼에서, 맨 위에 있는 Version이 아닌 Bundle Version Code를 통해 앱의 업데이트 여부를 확인하기 때문)
Identification > Minimum API Level은 자주 바뀌는 편이다. 자신이 앱을 출시할 플랫폼의 정책에 맞춰 설정하자.
Configuration > Scripting Backend를 목적에 맞춰 설정하자. 출시용이라면 IL2CPP로, 빌드 테스트용이라면 Mono로.
Configuration > Target Architectures에서 ARM64에 체크. Google Play Store에 출시하려면 필수다.
Publishing Settings
Project Keystore > Custom Keystore를 체크. 이후 Select... Browse...를 통해 잘 선택한 뒤 (Keystore) Password를 맞게 치면 Project Key > Alias를 선택할 수 있게 된다. 마찬가지로 Password를 맞게 쳐주면 출시 준비 완료!
마지막으로...
Edit > Project Settings > Player가 아닌, File > Build Profiles > Andriod로 가서 Platform Settings 밑에 있는 Build App Bundle (Google Play)를 체크해야 한다.
using UnityEngine;
using System;
using GoogleMobileAds;
using GoogleMobileAds.Api;
using UnityEngine.SceneManagement;
public class AdManager : MonoBehaviour
{
public static GameObject ad_manager;
private BannerView banner_view;
private void Awake()
{
if (ad_manager == null)
{
ad_manager = gameObject;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public void Start()
{
MobileAds.Initialize((InitializationStatus initStatus) => { }); // Initialize the Google Mobile Ads SDK.
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void RequestBanner()
{
#if UNITY_ANDROID
string _adUnitId = "ca-app-pub-3940256099942544/6300978111";
#elif UNITY_IPHONE
string _adUnitId = "ca-app-pub-3940256099942544/2934735716";
#else
string _adUnitId = "unused";
#endif
//Clean up banner ad before creating a new one.
if (banner_view != null)
{
banner_view.Destroy();
}
// Create a adaptively-sized banner at top of the screen
AdSize adaptiveSize = AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(AdSize.FullWidth);
banner_view = new BannerView(_adUnitId, adaptiveSize, AdPosition.Bottom);
// create our request used to load the ad.
var adRequest = new AdRequest();
// send the request to load the ad.
banner_view.LoadAd(adRequest); //Debug.Log("Loading banner ad.");
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "AD Scene")
{
RequestBanner();
}
else
{
if (banner_view != null)
{
banner_view.Destroy();
}
}
}
}
데이터를 담는 Unity 전용 Asset이다. 주로 Prefab을 쉽게 관리하기 위해 쓰인다. 그러나 이렇게만 보면 어떻게 사용해야 하는지 도무지 감이 잡히지 않을 것이다. 필자도 처음에는 그랬기 때문에, 예시를 들면서 진행하겠다.
예시 상황
여러 가지 Projectile을 만들고 싶다. 그래서 Projectile 클래스를 만들었다.
보통의 경우, 이 시점에서 두 가지 방법을 사용하여 다양한 Projectile을 만들 수 있다.
이 Projectile 클래스를 상속하여 Wooden Arrow와 Iron Arrow 클래스를 만든다. 그렇게 상속을 통해 만들어진 클래스들을 각각의 GameObject에 붙인 뒤, Prefab으로 만든다.
또는? Projectile 클래스에 damage, speed, gravity와 같은 필드 변수를 public 또는 [SerializedField] & private으로 만든다. 그 후, Inspector 창에서 수치를 직접 조절한 뒤, Prefab으로 만든다.
보통의 경우에는 위의 두 가지 방법이 매우 효과적인 해결책이 될 수 있다. 그러나 Scriptable Object를 알고 있는 사람의 관점에서는 그렇지 않다. 왜냐하면, Scriptable Object는 "변하는 데이터만 담아두는" Asset이기 때문이다.
Scriptable Object로 구현한 Projectile을 위한 데이터
using UnityEngine;
public enum ElementalType
{
None,
Fire,
Ice,
Spark
}
[CreateAssetMenu(fileName = "New Projectile Data", menuName = "Projectile Data")]
public class ProjectileData : ScriptableObject
{
public Sprite texture;
public float damage;
public Vector2 direction;
public float speed;
public float gravity;
public ElementalType elemental_type;
}
위와 같은 코드를 쓴 뒤, 아래 사진과 같이 조작하면 "Projectile을 위한 Data Asset(=Projectile Data)"을 만들 수 있다;
빈 GameObject를 생성한 뒤, Sprite Renderer, Projectile (Script), Rigidbody 2D 컴포넌트를 달아주자. 이름은 Projectile로 해주자. 그 후, 이렇게 만든 Projectile Script에 Projectile Data를 Inspector 창을 통해 넣어주면 된다.
간단한 Preview
Trail은 자연스럽게 보이도록 넣었다!
이제 이렇게 만든 Projectile을 Prefab으로 만든 뒤에, Instantiator를 만들어주면 "매우 쉽고 편하게" 다양한 종류의 Projectile을 만들 수 있다!
탑뷰로 진행되는 2D 게임에서, 그림자를 어떻게 구현해야 할지에 대한 가이드가 잘 없다. (그리고 애초에 이런 걸 쉽게 구현할 수 있는 방법이 Unity 6 이전까지는 존재하지가 않았다) 그러나 Unity 6으로 업그레이드 되면서, 2D 관련 Shadow 컴포넌트가 많은 업그레이드를 받았고, 그랬기에 매우 쉽게 구현할 수 있게 됐다! 그래서 오늘은 이 "쉬운 구현법"을 소개해보고자 한다.
이 가이드가 끝날 무렵에는 이런 것들을 마음대로 갖고 놀 수 있을 것이다;
벽 바깥쪽으로는 광원이 새어나가지 않는다!
Background Tilemap
먼저, 2D 게임에는 3D 게임의 Directional Light와 같은 개념이 없어서, 배경이 될 Object가 없으면, 자기 자신의 위에 비추는 Light가 아닌 경우, 아무런 Light 효과도 볼 수가 없다는 사실을 알아야 한다. 만약 Tilemap에 대해 잘 알지 못한다면, 다음 글을 보면 도움이 많이 될 것이다!
Hierarchy 창에서 우클릭을 한 뒤, 2D Object > Sprites > Square을 선택하여 Brick GameObject를 하나 만들어준다.
그 후, Shadow Caster 2D 컴포넌트를 추가한다.
Shadow Caster 2D 컴포넌트에서, Casting Option을 "Cast And Self Shadow"로 한다. 이 옵션은 자기 자신(Sprite 기준으로)의 위쪽에도 Shadow를 Cast하게 만든다.
Shadow Caster 2D 컴포넌트에서, Alpha Cutoff를 0으로 한다. (이걸 안 하면, "Cast And Self Shadow"를 사용했을 때 그림자 사이에 실같은 틈이 생긴다)
이제 이렇게 만든 GameObject를 Assets 창으로 드래그하여 Prefab로 만들면 된다.
Brick Tilemap
위에서 만들었던 Background Tilemap은 그대로 두고, 새로운 "Brick" Tilemap을 하나 만들자. 이 Tilemap에는 처음 만들었을 시에 기본적으로 추가되어있는 "Tilemap", "Tilemap Renderer" 컴포넌트 말고도, "Composite Shadow Caster 2D" 컴포넌트도 추가해야 한다.
이후 Tilemap Palette를 열고, 시선을 Tile Palette의 (적당한) 왼쪽 아래로 옮기면 "Default Brush"가 보일 것이다. 이건 어떤 브러쉬를 사용할지 결정하는 옵션이다. 이 브러쉬를 GameObject Brush로 바꾼 뒤, Cells를 클릭하면 "Element 0"이 보일 것이다.
이 Element 0을 클릭하면 여러 가지 것들이 보일텐데, 지금은 Game Object에 우리가 위에서 만든 Brick Prefab을 넣어주기만 하면 된다.
Prefab을 넣은 뒤, Tile Palette의 (적당한) 오른쪽 위로 시선을 옮기면 "연필/격자/구"가 보일 텐데, 여기서 "연필 모양"이 "Toggles Tile Palette Edit"이다. 이 모드를 활성화하면 Tilemap을 편집할 수 있다. 이걸 켜준 뒤 Palette의 아무 곳이나 클릭하면 Prefab이 Palette 위에 저장된다. (Toggles Tile Palette Edit 모드를 해제한 뒤) 이제 이걸 마음대로 Scene에 칠하면 된다.
Player
Hierarchy 창에서 우클릭을 한 뒤, 2D Object > Sprites > Square을 선택하여 Player GameObject를 하나 만들어준다.
Light 2D 컴포넌트를 추가해준다. Light Type은 "Spot"으로, Radius는 5 정도로 해준다.
기본적으로 Shadows는 Light 2D 컴포넌트에서 설정할 수 있는 옵션이므로, "이 객체가 발산하는 빛"에 대한 그림자가 어떻게 드리우도록 만들지 결정하는 것이다.
Strength는 그림자의 불투명도를 조절한다. 1이라면, 그림자가 드리운 영역이 완전히 검게 표시된다. 당연히 다른 오브젝트도 안 보인다. 0이라면, 그림자가 아예 안 보인다.
Softness는 "그림자-모서리"의 표현 방식을 결정한다. 이 값이 클수록 그림자의 모서리가 좀 더 부드럽게 보인다.
Falloff Strength는 Softness와 연계되는 옵션이다. 이 값이 작을수록 그림자의 모서리가 보다 퍼지는 느낌이 된다.
비록 Player GameObject는 그림자가 잘 적용되는지 테스트하기 위해 만든 것이지만, 움직이게 만들고 싶다면 다음과 같은 코드를 적용해주면 된다!
코드
작업중...
약간의 수정을 거친 뒤 더 좋아진 코드로 돌아오겠다!
Torch
Player GameObject와 완전히 똑같다. 다만 차이점이라면 움직일 수 없다는 정도?
조금 더 재미있게 사용해보고 싶다면, Torch가 지닌 Light 2D 컴포넌트에서, Color을 바꿔보면 좋다. 예를 들어, Torch를 2개 만든 뒤, 하나는 Color를 빨간색으로, 다른 하나는 파란색으로 한 뒤 Raduis를 좀 크게 해서 겹치는 범위를 만들어보면, 보라색이 되는 것을 확인할 수 있다. (분명 빛인데, 색처럼 섞인다!)