본인은 플밍 공부중인 접밥임

유니티 모노비헤비어에서 인풋받아서 게임오브젝트 조작하는거 좀 체계적으로 할 수 없을까 해서

나름 고민좀 해봤는데 괜찮은지 어떤지 조언좀 듣고 싶어서 올려봄

태클 환영이구 더 좋은 방식 있다면 알려주면 감사하겠슴...



0단계 : Actor
Actor라는 클래스 내에서 모든 처리를 다 한다.
인풋처리건 그에 따른 동작처리건. 가장 원시적인 단계

1단계 : Controller - Actor
실제 행위를 지시하는건 Controller가 한다. 
즉 Controller가 인풋을 받으면 actor.Move() actor.Jump() 등의 행위를 명령하고
실제 행위에 대한 구현은 Actor 내에 존재한다.
동작 방식의 변경이 필요할때 Actor는 그대로 놔두고 Controller만 변경해주면 되서 유연성이 확보됨. 
Controller는 능동적이며 Actor는 수동적이다.

2단계 : Controller - Behaviour - Actor
Behaviour에 Actor가 실행할 행위에 대한 구현이 들어가 있다. 컴포넌트 패턴.
즉 Move, Jump 에 대한 구현이 Behaviour에 정의된다.
Controller는 actor.GetBehaviour(), move.Move(10); 과 같이 실행시킨다.
이 상태에서는 actor라는 객체의 상태(actor)와 행위(behaviour)를 분리한 결과.
여기서는 Controller와 Actor가 약한결합으로 연결되어 있어서 특정 Behaviour가 없으면 그냥 처리 안하는 식의 동작이 가능하다.
하지만 Controller가 특정 Behaviour가 있는지 먼저 확인해야 해서 행위에 대한 FireAndForget이 불가능하다.
또한 행위를 데이터화 시키는 것도 어렵다.(스크립트 등)

3단계 : Controller - Execution - Behaviour - Actor
2단계의 단점을 보완하기 위해 "행위의 명세"를 중간에 추가한 형태.
Controller가 Execution을 생성하면 Execution이 실제로 자신이 필요한 Behaviour들을 참조하여 실행시킨다.
이렇게 하면 Controller는 Actor에 필요한 Behaviour가 있는지 없는지에 상관 없이 Execution만 생성해서 Actor로 넘겨주면 된다.
이 구조에서는 Execution을 생성하는 객체가 반드시 Controller일 필요도 없어서 행위의 Batch를 데이터화 시키는것이 가능하다.

이후 단계? 
3단계에서도 Controller에 자신이 조작할 Actor에 대한 참조를 들고 있어야 해서 커플링이 100% 사라지진 않는다.
만약 이것조차 없애고자 한다면 Actor에 ID를 배분하고 Execution이 해당 ID의 Actor를 검색하도록 하면 되지만
이것은 어떤 게임이냐에 따라 구현이 필요할 수도 있고 필요없을 수도 있어서 케바케일 듯.



예제 코드 

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

public class Actor : MonoBehaviour
{
    List behaviours = new List();
    ActorController controller = new NullActorController();

    List executions = new List();

    private void Awake()
    {
        behaviours.Add(new JumpBehaviour(this));
        SetController(new TestCharacterController());
    }

    private void Update()
    {
        controller.Update(this);

        if(executions.Count > 0 )
        {
            foreach(var execution in executions)
            {
                execution.Execute(this);
            }
            executions.Clear();
        }
    }

    public void AddExecution(IActorExecution execution)
    {
        executions.Add(execution);
    }

    public void AddExecution(IEnumerable executions)
    {
        this.executions.AddRange(executions);
    }

    public void SetController(ActorController controller)
    {
        this.controller = controller ?? new NullActorController();
        controller.Initialize(this);
    }

    public void AddBehaviour(ActorBehaviour behaviour)
    {
        behaviours.Add(behaviour);
    }

    public T GetBehaviour() where T: ActorBehaviour
    {
        var result = behaviours.Find(bhv => bhv is T);
        return result != null ? result as T : null;
    }
}


public abstract class ActorController
{
    public virtual void Initialize(Actor actor)
    {
        
    }

    public virtual void Update(Actor actor)
    {

    }
}

public class NullActorController : ActorController
{

}

public class TestCharacterController : ActorController
{
    public override void Initialize(Actor actor)
    {
        
    }

    public override void Update(Actor actor)
    {
        if(Input.GetAxis("Vertical") > 0.0f)
        {
            Debug.Log("UP");
            actor.AddExecution(new Exct_Jump(50.0f));
        }
    }
}

public abstract class ActorBehaviour
{
    //그냥 컨테이너에 담기 위함
    protected Actor targetActor { get; private set; }

    public ActorBehaviour(Actor actor)
    {
        targetActor = actor;
    }

    public virtual void OnInitialize()
    {

    }
}

public class MoveBehaviour : ActorBehaviour
{
    public MoveBehaviour(Actor actor) : base(actor)
    {

    }

    public void Move()
    {

    }
}

public class JumpBehaviour : ActorBehaviour
{
    public JumpBehaviour(Actor actor) : base(actor)
    {

    }

    public void Jump(float jumpPower)
    {
        var rigidBody = targetActor.gameObject.GetComponent();
        rigidBody.AddForce(new Vector3(0.0f, jumpPower, 0.0f));
    }
}

public interface IActorExecution
{
    void Execute(Actor actor);
}

public class Exct_Jump : IActorExecution
{
    float jumpPower;

    public Exct_Jump(float jumpPower)
    {
        this.jumpPower = jumpPower;
    }

    public void Execute(Actor actor)
    {
        var jump = actor.GetBehaviour();
        if (jump != null)
        {
            jump.Jump(jumpPower);
        }
    }
}

public class Exct_Move : IActorExecution
{
    Vector3 direction;

    public Exct_Move(Vector3 direction)
    {
        this.direction = direction;
    }

    public void Execute(Actor actor)
    {

    }

}