odyssey

#11 odyssey 개발일지 : parallax scrolling

san10 2023. 6. 22. 18:08

게임 시스템과 뼈대는 얼추 갖춰진 것 같아서 이제 컨셉아트에 맞춰서 그래픽 부분을 끼워넣으려고 한다..

 

parallax scrolling

구름이나 바다같은 소재는 뒤에 있고, 어떤 나무는 가까이, 어떤 나무는 멀리 있다.

2d에서 이런 원근감을 나타내기 위해,

멀리있는 소재는 느리게, 가까이 있는 소재는 빠르게 이동시켜서 원근감을 나타내는데

좀 찾아보니깐 이런 기법을 parallax scrolling 이라고 부르는 것 같다.

게임개발 뿐만아니라 웹 개발에서도 많이 사용하는듯 하다.

parallax scrolling 예시

 

 

구현

https://www.youtube.com/watch?v=zit45k6CUMk 

이 유튜브 강의를 참고해서 만들었다.

 

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

public class ParallaxEfect : MonoBehaviour
{
    private float startposX, startposY;
    public float parallaxFactorX;
    public float parallaxFactorY;
    public GameObject cam;

    void Start()
    {
        startposX = transform.position.x;
        startposY = transform.position.y;
    }

    
    void FixedUpdate()
    {
        float distanceX = cam.transform.position.x * parallaxFactorX;
        float distanceY = cam.transform.position.y * parallaxFactorY;
        Vector3 newPosition = new Vector3(startposX + distanceX, startposY+distanceY, transform.position.z);
        transform.position = newPosition;
    }

}

각 레이어(구름레이어, 바다레이어, 풀레이어 등등..)에 스크립트를 달아주고,

원하는 만큼 parallaxFactor를 조정한다.

 

 

그런데 막상 실행해보니 문제가 있었다..

아주 앞에있는 레이어나 뒤에있는 레이어(구름 바다등..)은 의도대로 잘 움직이지만

지형과 가까이 있는 레이어들은 둥둥 떠다니거나 땅에 묻히는 현상이 일어났다..

 

지형 자체가 곡선이라 오브젝트가 있어야 할 y축이 계속 바뀌어서 둥둥 뜨거나 묻히는 것 같았다.

그래서 해결방안을 2개 생각해봤는데..

첫번째는 지형과 가까이 있는 레이어에는 parallax scrolling을 적용하지 않는 거고,

두번째는 x축을 매개변수로 해서 곡선 지형에 맞게 움직이게 하면 될 것 같았다.

 

첫번째 방식으로 해본 결과..

너무 딱딱하고 밋밋하다는 생각이 들었다..

 

그래서 두번째 방식으로 구현하기 위해..

먼저 각 지형의 곡선의 궤적을 AnimationCurve로 나타냈다.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(TerrainCurve))]
public class TerrainCurveEditor : Editor
{

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        

        if (GUILayout.Button("TerrainCurveGererate"))
        {
            generateAnimationCurve();
        }
    }

    private void generateAnimationCurve()
    {
        TerrainCurve terrainCurve = (TerrainCurve)target;

        int terrainNum = terrainCurve.transform.childCount;
        Dictionary<int, List<Vector2>> curveDictionary = new Dictionary<int, List<Vector2>>();
        List<Vector2> curvePointList = new List<Vector2>();

        for (int i = 0; i < terrainNum; i++)
        {
            List<Vector2> curveList = new List<Vector2>(terrainCurve.transform.GetChild(i).GetComponent<PolygonCollider2D>().points);
            curveList.RemoveAt(0);
            curveList.RemoveAt(curveList.Count - 1);
            curveDictionary[i] = curveList;
        }

        Vector2 pointOffset = new Vector2(0, 0);
        pointOffset.y -= curveDictionary[0].First().y;

        for (int i=0; i < terrainNum; i++)
        {
            foreach(Vector2 point in curveDictionary[i])
            {
                curvePointList.Add(point + pointOffset);
            }

            if (i == terrainNum - 1)
                break;

            pointOffset= curvePointList.Last();
            pointOffset.y -= curveDictionary[i+1].First().y;
        }

        AnimationCurve animationTerrainCurve = new AnimationCurve();

        foreach(Vector2 point in curvePointList)
        {
            Keyframe keyframe = new Keyframe(point.x, point.y);
            animationTerrainCurve.AddKey(keyframe);
        }

        terrainCurve.animationTerrainCurve = animationTerrainCurve;
    }
}

지형의 point들을 List로 받아서 AnimationCurve로 나타낸다.

이렇게 지형 모양의 곡선을 얻었다! ^___^

 

이 곡선을 바탕으로,

기존의 ParallaxEffect 스크립트를 각 지형의 궤도에 맞게 움직이도록 수정했다.

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

public class CurveParallaxEffect : MonoBehaviour
{
    public Vector3 terrainStartPoint;
    public AnimationCurve terrainCurve;

    private float startposX, startposY;
    private float offsetX, offsetY;
    public float parallaxFactorX;
    public float parallaxFactorY;
    public GameObject cam;


    void Start()
    {
        startposX = transform.position.x;
        startposY = transform.position.y;
        offsetX = startposX - terrainStartPoint.x;
        offsetY = startposY - (terrainCurve.Evaluate(offsetX) + terrainStartPoint.y);
    }


    void Update()
    {
        float distanceX = cam.transform.position.x * parallaxFactorX;
        float distanceY = terrainCurve.Evaluate(offsetX+distanceX);
        Vector3 newPosition = new Vector3(startposX + distanceX, terrainStartPoint.y + distanceY+ offsetY, transform.position.z);
        transform.position = newPosition;
    }
}

 

 

스크립트를 원하는 오브젝트에 붙여주면..

오브젝트들이 궤적을 따라 움직인다! ^___^

어쨌든 이걸 바탕으로 만든 최종 결과물은..

그런데 생각보다 별로라고 느꼈다.

왜냐하면..

1. 생각보다 원근 효과가 미미하다

2. 그렇다고해서 parallaxFactor를 높여서 움직임을 크게 만들면 어색하게 보인다.

생각보다 오브젝트가 궤적을 따라 움직이는게 묘하게 이상하고 어색함

3. 처음보단 y축이 안맞는 문제가 덜해졌지만 완전히 사라진건 아니다.

 

 

그래서..

그냥 지형과 가까이 있는 오브젝트는 parallax scrolling 효과를 빼고

앞과 뒤에있는 오브젝트에만 적용했다..

원근감을 주고싶으면 억지로 오브젝트를 궤적에 맞게 움직이기 보다는

추가적인 꾸미기용 레이어층을 넣는게 맞는것 같다

 

나름 고민도 많이하고 꽤 오래 걸렸는데

뻘짓한 기분이다^___^