[Unity] 2D Shooting
1. Link
깃허브 주소 : https://github.com/kimchosungjun/Toy_2D_Shooting.git
플레이 영상 : https://youtube.com/shorts/aCYT8Qj98GM?feature=share
- YouTube
www.youtube.com
2. Issue
- Limiter
- Ranking
- Boss Pattern
- Infinite Scrolling
3. 설명
1) Limiter
플레이어와 적 그리고 아이템이 화면 밖으로 나가지 않게 경계선을 제한하는 기능을 구현했습니다.
초기에 Viewport의 최대, 최소 지점(경계선)을 지정하고 이를 World 좌표로 변환하였습니다.
//ViewPort의 경계선을 World 좌표로 변환했습니다.
[SerializeField] Vector2 viewPortLimitMin;
[SerializeField] Vector2 viewPortLimitMax;
private void initWorldPos()
{
worldPosLimitMin = cam.ViewportToWorldPoint(viewPortLimitMin);
worldPosLimitMax = cam.ViewportToWorldPoint(viewPortLimitMax);
}
캐릭터가 화면 밖으로 나가지 못하도록 현재 위치가 최대, 최소 Viewport 지점을 넘어서지 못하게 하였습니다.
public Vector3 checkMovePosition(Vector3 _pos, bool _isBoss = false)
{
Vector3 viewPortPos = cam.WorldToViewportPoint(_pos);
if (viewPortPos.x < (_isBoss == false ? viewPortLimitMin.x : viewPortLimitMinBoss.x))//0~1
{
viewPortPos.x = (_isBoss == false ? viewPortLimitMin.x : viewPortLimitMinBoss.x);
}
else if (viewPortPos.x > (_isBoss == false ? viewPortLimitMax.x : viewPortLimitMaxBoss.x))
{
viewPortPos.x = (_isBoss == false ? viewPortLimitMax.x : viewPortLimitMaxBoss.x);
}
if (viewPortPos.y < viewPortLimitMin.y)
{
viewPortPos.y = viewPortLimitMin.y;
}
else if (viewPortPos.y > viewPortLimitMax.y)
{
viewPortPos.y = viewPortLimitMax.y;
}
return cam.ViewportToWorldPoint(viewPortPos);
}
아이템의 경우, x와 y좌표 중 어느 방향이 경계에 닿았는지를 Tuple로 반환하였습니다.
public (bool _x, bool _y) IsReflectItem(Vector3 _pos, Vector3 _dir)
{
bool rX = false;
bool rY = false;
if ((_pos.x < worldPosLimitMin.x && _dir.x < 0) || (_pos.x > worldPosLimitMax.x && _dir.x > 0))
{
rX = true;
}
if ((_pos.y < worldPosLimitMin.y && _dir.y < 0) || (_pos.y > worldPosLimitMax.y && _dir.y > 0))
{
rY = true;
}
return (rX, rY);
}
2) Ranking
PlayerPrefers를 활용해 레지스터에 플레이어의 이름과 점수를 담은 클래스를 저장하고, 이를 불러오는 랭킹 시스템을 만들었습니다. 아래는 랭킹 키를 통해 저장된 랭킹 정보를 불러오는 스크립트 입니다.
private void initRankView()
{
List<cUserData> listUserData = null;
clearRankView();
if (PlayerPrefs.HasKey(Tool.rankKey) == true)
{
listUserData = JsonConvert.DeserializeObject<List<cUserData>>(PlayerPrefs.GetString(Tool.rankKey));
}
else
{
listUserData = new List<cUserData>();
int rankCount = Tool.rankCount;
for (int iNum = 0; iNum < rankCount; ++iNum)
{
listUserData.Add(new cUserData() { Name = "None", Score = 0 });
}
string value = JsonConvert.SerializeObject(listUserData);
PlayerPrefs.SetString(Tool.rankKey, value);
}
int count = listUserData.Count;
for (int iNum = 0; iNum < count; ++iNum)
{
cUserData data = listUserData[iNum];
GameObject go = Instantiate(fabRank, contents);
FabRanking goSc = go.GetComponent<FabRanking>();
goSc.SetData((iNum + 1).ToString(), data.Name, data.Score);
}
}
3) Boss Pattern
보스는 3가지의 패턴을 가지도록 구상하였고, 한 패턴에 돌입했다면 중복해서 실행되지 않게 구현했습니다.
protected override void shooting()
{
// 중복 방지
if (isMovingTrsBossPosition == false)
{
return;
}
timer += Time.deltaTime;
if (patternChange == true)
{
if (timer >= 1.0f)
{
timer = 0.0f;
patternChange = false;
}
return;
}
// 패턴 실행
if (curPattern == 1)// First Pattern
{
if (timer >= pattern1Reload)
{
timer = 0.0f;
shootStraight();
if (curPatternShootCount >= pattern1Count)
{
curPattern++;
patternChange = true;
curPatternShootCount = 0;
}
}
}
else if{ .. }
else { .. }
}
4) Infinite Scrolling
무한 스크롤링을 만드는 방법으로 두 스프라이트를 붙이고 이동시키는 방법을 주로 사용했었는데, Unlit/Transparent Shader를 가진 Material 사용해서 구현할 수 있다는 것을 알고 이를 이용하여 구현했습니다.
Material Offset의 값을 반복시켜 무한스크롤링을 구현했습니다.
Material matTop;
Material matMid;
Material matBot;
public void InfiniteMap()
{
Vector2 vecTop = matTop.mainTextureOffset;
Vector2 vecMid = matMid.mainTextureOffset;
Vector2 vecBot = matBot.mainTextureOffset;
vecTop += new Vector2(0, speedTop * Time.deltaTime);
vecMid += new Vector2(0, speedMid * Time.deltaTime);
vecBot += new Vector2(0, speedBot * Time.deltaTime);
vecTop.y = Mathf.Repeat(vecTop.y, 1.0f);
vecMid.y = Mathf.Repeat(vecMid.y, 1.0f);
vecBot.y = Mathf.Repeat(vecBot.y, 1.0f);
matTop.mainTextureOffset = vecTop;
matMid.mainTextureOffset = vecMid;
matBot.mainTextureOffset = vecBot;
}